Why java += get wrong result, and how can I prevent this? - java

Why java += get wrong result, and how can I prevent this problem? (for example any way show warning in IDE?)
I tried eclipse & IntelliJ but both not show any warning.
Sample code:
{
long a = 20000000000000000L;
double b = 90.0;
a += b;
System.out.println(a); // 20000000000000088 NG
}
{
long a = 10000000000000000L;
double b = 90.0;
a += b;
System.out.println(a); // 10000000000000090 OK
}
{
long a = 20000000000000000L;
double b = 90.0;
a += (long) b;
System.out.println(a); // 20000000000000090 OK
}

According to the JLS, this compound assignment expression
a += b;
is equivalent to
a = (long) (a + b);
Since b is a double, binary numeric promotion occurs before the addition happens. Both operands are converted to double values and the addition happens with floating point arithmetic.
The value 20000000000000090 cannot be represented exactly as a double and therefore you lose precision, getting 20000000000000088 instead. It then gets cast back to a long which has enough precision to represent 20000000000000088.
Your second snippet produces 10000000000000090 which can be represented exactly as a double.
Your third snippet is equivalent to
a = (long) (a + (long) b);
which uses integer arithmetic. 20000000000000090 can be represented as a long.
I don't know of any tools to warn you about this.
Related:
Is floating point math broken?

Java uses 64-bit IEEE floating point numbers, which use 53 binary digits to represent the significant. In the first example,
{
long a = 20000000000000000L;
double b = 90.0;
a += b;
System.out.println(a); // 20000000000000088 NG
}
Here the variable a is converted to a double that must use an exponent greater than 1 to represent it, and it will no longer be an exact representation of the value 20000000000000000L.
In this example, it is possible to represent a using only the significand and an exponent of 1.
{
long a = 10000000000000000L;
double b = 90.0;
a += b;
System.out.println(a); // 10000000000000090 OK
}
In this example, b is explicitly converted to a long, avoiding the need to convert a to a double and perform floating point arithmetic.
{
long a = 20000000000000000L;
double b = 90.0;
a += (long) b;
System.out.println(a); // 20000000000000090 OK
}

As not all programmer(like me) can understand binary numeric promotion、
For preventing such += coding missing, I found a simple way: just forbid all += as coding rule.
I just grep all += in my projects, and change a += b to a = a + b.
IDE shows errors while type not match while + operation, as bellow:

Related

byte y=y+x; Results in error but y+=x; Compiles successfully. Why? [duplicate]

Until today, I thought that for example:
i += j;
Was just a shortcut for:
i = i + j;
But if we try this:
int i = 5;
long j = 8;
Then i = i + j; will not compile but i += j; will compile fine.
Does it mean that in fact i += j; is a shortcut for something like this
i = (type of i) (i + j)?
As always with these questions, the JLS holds the answer. In this case §15.26.2 Compound Assignment Operators. An extract:
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.
An example cited from §15.26.2
[...] 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);
In other words, your assumption is correct.
A good example of this casting is using *= or /=
byte b = 10;
b *= 5.7;
System.out.println(b); // prints 57
or
byte b = 100;
b /= 2.5;
System.out.println(b); // prints 40
or
char ch = '0';
ch *= 1.1;
System.out.println(ch); // prints '4'
or
char ch = 'A';
ch *= 1.5;
System.out.println(ch); // prints 'a'
Very good question. The Java Language specification confirms your suggestion.
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);
Yes,
basically when we write
i += l;
the compiler converts this to
i = (int)(i + l);
I just checked the .class file code.
Really a good thing to know
you need to cast from long to int explicitly in case of i = i + l then it will compile and give correct output. like
i = i + (int)l;
or
i = (int)((long)i + l); // this is what happens in case of += , dont need (long) casting since upper casting is done implicitly.
but in case of += it just works fine because the operator implicitly does the type casting from type of right variable to type of left variable so need not cast explicitly.
The problem here involves type casting.
When you add int and long,
The int object is casted to long & both are added and you get long object.
but long object cannot be implicitly casted to int. So, you have to do that explicitly.
But += is coded in such a way that it does type casting. i=(int)(i+m)
In Java type conversions are performed automatically when the type of the expression on the right hand side of an assignment operation can be safely promoted to the type of the variable on the left hand side of the assignment. Thus we can safely assign:
byte -> short -> int -> long -> float -> double.
The same will not work the other way round. For example we cannot automatically convert a long to an int because the first requires more storage than the second and consequently information may be lost. To force such a conversion we must carry out an explicit conversion.
Type - Conversion
Sometimes, such a question can be asked at an interview.
For example, when you write:
int a = 2;
long b = 3;
a = a + b;
there is no automatic typecasting. In C++ there will not be any error compiling the above code, but in Java you will get something like Incompatible type exception.
So to avoid it, you must write your code like this:
int a = 2;
long b = 3;
a += b;// No compilation error or any exception due to the auto typecasting
The main difference is that with a = a + b, there is no typecasting going on, and so the compiler gets angry at you for not typecasting. But with a += b, what it's really doing is typecasting b to a type compatible with a. So if you do
int a=5;
long b=10;
a+=b;
System.out.println(a);
What you're really doing is:
int a=5;
long b=10;
a=a+(int)b;
System.out.println(a);
Subtle point here...
There is an implicit typecast for i+j when j is a double and i is an int.
Java ALWAYS converts an integer into a double when there is an operation between them.
To clarify i+=j where i is an integer and j is a double can be described as
i = <int>(<double>i + j)
See: this description of implicit casting
You might want to typecast j to (int) in this case for clarity.
Java Language Specification defines E1 op= E2 to be equivalent to E1 = (T) ((E1) op (E2)) where T is a type of E1 and E1 is evaluated once.
That's a technical answer, but you may be wondering why that's a case. Well, let's consider the following program.
public class PlusEquals {
public static void main(String[] args) {
byte a = 1;
byte b = 2;
a = a + b;
System.out.println(a);
}
}
What does this program print?
Did you guess 3? Too bad, this program won't compile. Why? Well, it so happens that addition of bytes in Java is defined to return an int. This, I believe was because the Java Virtual Machine doesn't define byte operations to save on bytecodes (there is a limited number of those, after all), using integer operations instead is an implementation detail exposed in a language.
But if a = a + b doesn't work, that would mean a += b would never work for bytes if it E1 += E2 was defined to be E1 = E1 + E2. As the previous example shows, that would be indeed the case. As a hack to make += operator work for bytes and shorts, there is an implicit cast involved. It's not that great of a hack, but back during the Java 1.0 work, the focus was on getting the language released to begin with. Now, because of backwards compatibility, this hack introduced in Java 1.0 couldn't be removed.

How to test if value stored in double fit in long? (rounding yes, but truncating no)

I think that question is pretty straight. but here is an examples.
Example below is OK. I can take rounding and no truncating was done here.
public static void main(String[] args) {
double d = 9.9;
long l = (long)d;
System.out.println(l);
}
Output:
9
And now number out of range of long:
public static void main(String[] args) {
double d = 99999999999999999999999999999999.9;
long l = (long)d;
System.out.println(l);
}
Output:
9223372036854775807
This one troubles me. I cannot continue work with completely different number. I would rather get an error or an exception.
Is there any way to detect this in Java?
You can compare it with Long.MIN_VALUE and Long.MAX_VALUE:
public static boolean fitsLong(double d) {
return d >= Long.MIN_VALUE && d < Long.MAX_VALUE;
}
Somewhat more sofisticated approach is to use BigDecimal:
double value = 1234567.9;
long l = BigDecimal.valueOf(value)
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // 1234568
double value = 99999999999999999999999999999999.9;
long l = BigDecimal.valueOf(value)
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ArithmeticException
This way you can control how the rounding is performed.
You may ask, why there's strict inequality in fitsLong: d < Long.MAX_VALUE. Actually that's because the Long.MAX_VALUE itself cannot be represented as double number. When you cast (double)Long.MAX_VALUE, there's not enough precision in double type to represent it, so the closest representable value is selected which is 9223372036854775808.0 (Long_MAX_VALUE+1.0). So were it d <= Long.MAX_VALUE it would return true for number which is actually a little bigger as in this comparison Long.MAX_VALUE constant is promoted to double type. On the other hand Long.MIN_VALUE can be exactly represented in double type, thus here we have >=.
Also it's interesting why the following works:
double value = -9223372036854775809.9; // Long.MIN_VALUE-1.9
System.out.println(fitsLong(value)); // returns true
That's because you actually did not subtract anything from the Long.MIN_VALUE. See:
double d1 = Long.MIN_VALUE;
double d2 = -9223372036854775809.9;
System.out.println(d1 == d2); // true
The double precision is not enough to distinguish between -9223372036854775808 and -9223372036854775809.9, so it's actually the same double number. During the compilation it's converted to binary form, and binary form for these two numbers is the same. Thus having compiled program you cannot distinguish whether -9223372036854775808 or -9223372036854775809.9 was in the source code.
If you feel that it's still the issue, construct the BigDecimal from the String:
long l = new BigDecimal("-9223372036854775808.2")
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ok, -9223372036854775808
long l = new BigDecimal("-9223372036854775808.9")
.setScale(0, RoundingMode.HALF_EVEN)
.longValueExact(); // ArithmeticException
When you cast a floating point type to an int or long, the result is either the nearest integer (rounding towards zero), or the MIN_VALUE or MAX_VALUE for int or long. See JLS 5.1.3.
Hence, one alternative would be to do the typecast and then test for the appropriate MIN_VALUE or MAX_VALUE.
Note that Long.MAX_VALUE is 9223372036854775807 ... which is the number your test program outputs!
(However, this doesn't work if you are casting a floating point type to byte, char or short. See the link above for the explanation.)

Compilation error for the same statement? [duplicate]

Until today, I thought that for example:
i += j;
Was just a shortcut for:
i = i + j;
But if we try this:
int i = 5;
long j = 8;
Then i = i + j; will not compile but i += j; will compile fine.
Does it mean that in fact i += j; is a shortcut for something like this
i = (type of i) (i + j)?
As always with these questions, the JLS holds the answer. In this case §15.26.2 Compound Assignment Operators. An extract:
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.
An example cited from §15.26.2
[...] 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);
In other words, your assumption is correct.
A good example of this casting is using *= or /=
byte b = 10;
b *= 5.7;
System.out.println(b); // prints 57
or
byte b = 100;
b /= 2.5;
System.out.println(b); // prints 40
or
char ch = '0';
ch *= 1.1;
System.out.println(ch); // prints '4'
or
char ch = 'A';
ch *= 1.5;
System.out.println(ch); // prints 'a'
Very good question. The Java Language specification confirms your suggestion.
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);
Yes,
basically when we write
i += l;
the compiler converts this to
i = (int)(i + l);
I just checked the .class file code.
Really a good thing to know
you need to cast from long to int explicitly in case of i = i + l then it will compile and give correct output. like
i = i + (int)l;
or
i = (int)((long)i + l); // this is what happens in case of += , dont need (long) casting since upper casting is done implicitly.
but in case of += it just works fine because the operator implicitly does the type casting from type of right variable to type of left variable so need not cast explicitly.
The problem here involves type casting.
When you add int and long,
The int object is casted to long & both are added and you get long object.
but long object cannot be implicitly casted to int. So, you have to do that explicitly.
But += is coded in such a way that it does type casting. i=(int)(i+m)
In Java type conversions are performed automatically when the type of the expression on the right hand side of an assignment operation can be safely promoted to the type of the variable on the left hand side of the assignment. Thus we can safely assign:
byte -> short -> int -> long -> float -> double.
The same will not work the other way round. For example we cannot automatically convert a long to an int because the first requires more storage than the second and consequently information may be lost. To force such a conversion we must carry out an explicit conversion.
Type - Conversion
Sometimes, such a question can be asked at an interview.
For example, when you write:
int a = 2;
long b = 3;
a = a + b;
there is no automatic typecasting. In C++ there will not be any error compiling the above code, but in Java you will get something like Incompatible type exception.
So to avoid it, you must write your code like this:
int a = 2;
long b = 3;
a += b;// No compilation error or any exception due to the auto typecasting
The main difference is that with a = a + b, there is no typecasting going on, and so the compiler gets angry at you for not typecasting. But with a += b, what it's really doing is typecasting b to a type compatible with a. So if you do
int a=5;
long b=10;
a+=b;
System.out.println(a);
What you're really doing is:
int a=5;
long b=10;
a=a+(int)b;
System.out.println(a);
Subtle point here...
There is an implicit typecast for i+j when j is a double and i is an int.
Java ALWAYS converts an integer into a double when there is an operation between them.
To clarify i+=j where i is an integer and j is a double can be described as
i = <int>(<double>i + j)
See: this description of implicit casting
You might want to typecast j to (int) in this case for clarity.
Java Language Specification defines E1 op= E2 to be equivalent to E1 = (T) ((E1) op (E2)) where T is a type of E1 and E1 is evaluated once.
That's a technical answer, but you may be wondering why that's a case. Well, let's consider the following program.
public class PlusEquals {
public static void main(String[] args) {
byte a = 1;
byte b = 2;
a = a + b;
System.out.println(a);
}
}
What does this program print?
Did you guess 3? Too bad, this program won't compile. Why? Well, it so happens that addition of bytes in Java is defined to return an int. This, I believe was because the Java Virtual Machine doesn't define byte operations to save on bytecodes (there is a limited number of those, after all), using integer operations instead is an implementation detail exposed in a language.
But if a = a + b doesn't work, that would mean a += b would never work for bytes if it E1 += E2 was defined to be E1 = E1 + E2. As the previous example shows, that would be indeed the case. As a hack to make += operator work for bytes and shorts, there is an implicit cast involved. It's not that great of a hack, but back during the Java 1.0 work, the focus was on getting the language released to begin with. Now, because of backwards compatibility, this hack introduced in Java 1.0 couldn't be removed.

Implicit typecasting - Int to double

I've got an excercise from university which looks like:
int a = 10;
int b = 3;
double c = a / b;
The question is: Which value is c.
Now I would say, c is 3.3. It's casted implicit to double before calculating the result.
But the correct answer to this question is according to my records 3.0.
How this can be? Does the compiler really calculate the result first as integer and then in a second step casts it to double?
Or did I understand that incorrectly?
Does the compiler really calculate the result first as integer and
then in a second step casts it to double?
Yes
Does the compiler really calculate the result first as integer and
then in a second step casts it to double?
Yes,
Runtime first calculates the RHS result and then converts the result to double. Now in your case as RHS contains int / int so the result is in int and you don't get 3.3.
So if RHS contains double / int or int / double, the type promotion occurs and RHS operands are promoted to double before calculating the result and hence you get 3.3
See what is actually happening is :
double c = (double) a / b; //double of 3 = 3.0
you have to do
double c = a/(double)b

Why don't Java's +=, -=, *=, /= compound assignment operators require casting?

Until today, I thought that for example:
i += j;
Was just a shortcut for:
i = i + j;
But if we try this:
int i = 5;
long j = 8;
Then i = i + j; will not compile but i += j; will compile fine.
Does it mean that in fact i += j; is a shortcut for something like this
i = (type of i) (i + j)?
As always with these questions, the JLS holds the answer. In this case §15.26.2 Compound Assignment Operators. An extract:
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.
An example cited from §15.26.2
[...] 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);
In other words, your assumption is correct.
A good example of this casting is using *= or /=
byte b = 10;
b *= 5.7;
System.out.println(b); // prints 57
or
byte b = 100;
b /= 2.5;
System.out.println(b); // prints 40
or
char ch = '0';
ch *= 1.1;
System.out.println(ch); // prints '4'
or
char ch = 'A';
ch *= 1.5;
System.out.println(ch); // prints 'a'
Very good question. The Java Language specification confirms your suggestion.
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);
Yes,
basically when we write
i += l;
the compiler converts this to
i = (int)(i + l);
I just checked the .class file code.
Really a good thing to know
you need to cast from long to int explicitly in case of i = i + l then it will compile and give correct output. like
i = i + (int)l;
or
i = (int)((long)i + l); // this is what happens in case of += , dont need (long) casting since upper casting is done implicitly.
but in case of += it just works fine because the operator implicitly does the type casting from type of right variable to type of left variable so need not cast explicitly.
The problem here involves type casting.
When you add int and long,
The int object is casted to long & both are added and you get long object.
but long object cannot be implicitly casted to int. So, you have to do that explicitly.
But += is coded in such a way that it does type casting. i=(int)(i+m)
In Java type conversions are performed automatically when the type of the expression on the right hand side of an assignment operation can be safely promoted to the type of the variable on the left hand side of the assignment. Thus we can safely assign:
byte -> short -> int -> long -> float -> double.
The same will not work the other way round. For example we cannot automatically convert a long to an int because the first requires more storage than the second and consequently information may be lost. To force such a conversion we must carry out an explicit conversion.
Type - Conversion
Sometimes, such a question can be asked at an interview.
For example, when you write:
int a = 2;
long b = 3;
a = a + b;
there is no automatic typecasting. In C++ there will not be any error compiling the above code, but in Java you will get something like Incompatible type exception.
So to avoid it, you must write your code like this:
int a = 2;
long b = 3;
a += b;// No compilation error or any exception due to the auto typecasting
The main difference is that with a = a + b, there is no typecasting going on, and so the compiler gets angry at you for not typecasting. But with a += b, what it's really doing is typecasting b to a type compatible with a. So if you do
int a=5;
long b=10;
a+=b;
System.out.println(a);
What you're really doing is:
int a=5;
long b=10;
a=a+(int)b;
System.out.println(a);
Subtle point here...
There is an implicit typecast for i+j when j is a double and i is an int.
Java ALWAYS converts an integer into a double when there is an operation between them.
To clarify i+=j where i is an integer and j is a double can be described as
i = <int>(<double>i + j)
See: this description of implicit casting
You might want to typecast j to (int) in this case for clarity.
Java Language Specification defines E1 op= E2 to be equivalent to E1 = (T) ((E1) op (E2)) where T is a type of E1 and E1 is evaluated once.
That's a technical answer, but you may be wondering why that's a case. Well, let's consider the following program.
public class PlusEquals {
public static void main(String[] args) {
byte a = 1;
byte b = 2;
a = a + b;
System.out.println(a);
}
}
What does this program print?
Did you guess 3? Too bad, this program won't compile. Why? Well, it so happens that addition of bytes in Java is defined to return an int. This, I believe was because the Java Virtual Machine doesn't define byte operations to save on bytecodes (there is a limited number of those, after all), using integer operations instead is an implementation detail exposed in a language.
But if a = a + b doesn't work, that would mean a += b would never work for bytes if it E1 += E2 was defined to be E1 = E1 + E2. As the previous example shows, that would be indeed the case. As a hack to make += operator work for bytes and shorts, there is an implicit cast involved. It's not that great of a hack, but back during the Java 1.0 work, the focus was on getting the language released to begin with. Now, because of backwards compatibility, this hack introduced in Java 1.0 couldn't be removed.

Categories