Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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
    }
like image 796
andyf Avatar asked Jun 02 '16 02:06

andyf


2 Answers

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?
like image 145
2 revs Avatar answered Nov 11 '22 02:11

2 revs


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
}
like image 23
Josh Avatar answered Nov 11 '22 02:11

Josh