Varying behavior for possible loss of precision
Asked Answered
K

1

32

In Java, when you do

int b = 0;
b = b + 1.0;

You get a possible loss of precision error. But why is it that if you do

int b = 0;
b += 1.0;

There isn't any error?

Kuehnel answered 23/4, 2010 at 7:8 Comment(1)
Maybe, and just maybe it is because in the former it is converting b to floating point, then adding, then converting it back to integer ? And the latter it may be converting 1.0 to integer instead, and doing integer addition? Just a guess though.Amur
I
36

That's because b += 1.0; is equivalent to b = (int) ((b) + (1.0));. The narrowing primitive conversion (JLS 5.1.3) is hidden in the compound assignment operation.

JLS 15.26.2 Compound Assignment Operators (JLS Third Edition):

A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.

For example, the following code is correct:

short x = 3;
x += 4.6;

and results in x having the value 7 because it is equivalent to:

short x = 3;
x = (short)(x + 4.6);

This also explains why the following code compiles:

byte b = 1;
int x = 5;
b += x; // compiles fine!

But this doesn't:

byte b = 1;
int x = 5;
b = b + x; // DOESN'T COMPILE!

You need to explicitly cast in this case:

byte b = 1;
int x = 5;
b = (byte) (b + x); // now it compiles fine!

It's worth noting that the implicit cast in compound assignments is the subject of Puzzle 9: Tweedledum from the wonderful book Java Puzzlers. Here are some excerpt from the book (slightly edited for brevity):

Many programmers think that x += i; is simply a shorthand for x = x + i;. This isn't quite true: if the type of the result is wider than that of the variable, the compound assignment operator performs a silent narrowing primitive conversion.

To avoid unpleasant surprises, do not use compound assignment operators on variables of type byte, short, or char. When using compound assignment operators on variables of type int, ensure that the expression on the right-hand side is not of type long, float, or double. When using compound assignment operators on variables of type float, ensure that the expression on the right-hand side is not of type double. These rules are sufficient to prevent the compiler from generating dangerous narrowing casts.

For language designers, it is probably a mistake for compound assignment operators to generate invisible casts; compound assignments where the variable has a narrower type than the result of the computation should probably be illegal.

The last paragraph is worth noting: C# is a lot more strict in this regard (see C# Language Specification 7.13.2 Compound assignment).

Iconoscope answered 23/4, 2010 at 7:12 Comment(6)
Thanks. It's hard to search for documentation when the keyword you want to use is something like "+=". :PKuehnel
All op= operators are called "compound assignment" in Java. But yes, I do think that querying for symbols instead of keywords is hard in most search engines.Iconoscope
Any googlers here? I'd like a search box which also takes into account symbols like + = < and > :)Televise
Yeah, I wish there'd be a way to use regular google with regular expressions like in google code search. But then again, they'd need to change their whole indexing system.Kuehnel
I just got caught out by this behaviour, and was wondering why Java designers added this implicit conversion? What are the benefits of doing it this way? It seems too strange to actively add in extra behaviour that has no upside...Merrygoround
@mallardz, What else do you expect? This is the least surprising behavior, considering that Java is statically typed and the result has to fit in the destination type. The only alternative is a compile error, which isn't user friendly at all. Plus it matches C, where if you have an unsigned char x and you do x++, you aren't expecting to get an int back.Fredela

© 2022 - 2024 — McMap. All rights reserved.