[Fixed] x = x*0.90; gives lossy conversion error. x*=0.90; does not. Why? – Java

by
Liam Thompson
assignment-operator double integer java multiplication

The Solutions:

Solution 1: Implied Cast in Compound Assignment Operators

The Java Language Specification (JLS) defines the behavior of compound assignment operators such as *=, +=, and -=. According to the JLS, when using these operators, an implicit cast is performed to convert the right-hand operand to the type of the left-hand operand.

This means that in the expression x *= 0.90;, the double value 0.90 is implicitly cast to int before being multiplied by x. This results in the correct result, as x is an int variable and can only hold integer values.

On the other hand, in the expression x = x * 0.90;, the double value 0.90 is not implicitly cast to int, and the result of the multiplication is a double. Since x is an int variable, assigning a double to it results in a lossy conversion error, as the fractional part of the double will be lost.

To fix this error, you can explicitly cast the double value 0.90 to int using the expression (int)0.90 before performing the multiplication. This will ensure that the result of the multiplication is an int and can be assigned to x without a lossy conversion error.

Solution 2: Java’s implicit conversion

Java’s implicit conversion rules sometimes lead to unexpected results. This is especially true when working with different data types, such as int and double.

In the first example, the expression x *= 0.90; is valid because the result of the multiplication is a double, which is then implicitly converted to an int before being assigned to x. This is allowed because Java automatically widens primitive data types to accommodate larger values.

In the second example, the expression x = x * 0.90; is invalid because the result of the multiplication is a double, and there is no implicit conversion from double to int. To fix this error, you would need to explicitly cast the result of the multiplication to an int using a cast operator, such as (int).

Here is a modified version of your code that uses an explicit cast:

int x = 18;
x = (int) (x * 0.90);
System.out.println(x);

This code will print 16, just like the first example.

The lesson here is to be aware of Java’s implicit conversion rules and to use explicit casts when necessary to avoid errors.

Solution 3: Concise Assignment without Conversion Errors

The crux of the issue is in the difference between the explicit assignment operator = and the compound assignment operator *=. The explicit assignment operator, x = x * 0.90;, leads to a lossy conversion error because it requires an explicit conversion from double (the result of x * 0.90) to int (the type of x). This conversion is risky as it may result in information loss when the fractional part of the double value is truncated.

In contrast, the compound assignment operator, x *= 0.90;, elegantly handles this situation. It first implicitly converts the constant 0.90 from double to int. Since 0.90 is an exact integer representation, this conversion is safe and lossless. The product of x and 0.90 is then computed using integer arithmetic, eliminating any lossy conversion.

To highlight this, consider the following examples:

  • Lossy Conversion Error with Explicit Assignment:
int x = 5;
x = x * 0.90; // Error: lossy conversion from double to int

Here, the attempt to assign the result of x * 0.90 directly to x triggers a lossy conversion error because x * 0.90 is a double and x is an int.

  • No Error with Compound Assignment:
int x = 5;
x *= 0.90; // No error: implicit conversion handled by compound assignment

With the compound assignment *=, the conversion from double to int happens implicitly, and it’s guaranteed to be lossless because the constant 0.90 can be precisely represented as an integer.

Q&A

Why using compound assignment x *= 0.90; does not give a lossy conversion error but x = x * 0.90; does?

The Java Language Specification implicitly casts the result of a compound assignment operation.

What is the reason behind this design decision in Java?

To maintain consistency in casting rules across different types and operators.

Can you provide an example to illustrate the difference?

With x *= 0.90;, the result is implicitly converted to an int, while with x = x * 0.90;, the explicit assignment requires a cast to avoid a lossy conversion error.