Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Java RoundingMode.HALF_EVEN bug?

Tags:

java

When I rounded a double number with HALF_EVEN mode , I found a problem, I don't know whether it is a bug of JDK? Please see the following code:

public static void main(String[] args) {
    RoundingMode mode = RoundingMode.HALF_EVEN;
    for (int i = 10; i < 100; i++) {
        double d = i + 0.55;
        int scale = 1;
        process(d, scale++, mode);

        d = i + 0.555;
        process(d, scale++, mode);

        d = i + 0.5555;
        process(d, scale++, mode);

        d = i + 0.55555;
        process(d, scale++, mode);
        System.out.println("\n");
    }
}

private static void process(double d, int scale, RoundingMode roundingMode) {
    BigDecimal b = new BigDecimal(d).setScale(scale, roundingMode);
    System.out.println(d + " -> " + b);
}

I expect to output:

10.55 -> 10.6
10.555 -> 10.56
10.5555 -> 10.556
10.55555 -> 10.5556


11.55 -> 11.6
11.555 -> 11.56
11.5555 -> 11.556
11.55555 -> 11.5556

.....

But actually, it outputs data like the below:

10.55 -> 10.6
10.555 -> 10.55
10.5555 -> 10.556
10.55555 -> 10.5556


11.55 -> 11.6
11.555 -> 11.55
11.5555 -> 11.556
11.55555 -> 11.5556
....

30.55 -> 30.6
30.555 -> 30.55
30.5555 -> 30.555
30.55555 -> 30.5556



31.55 -> 31.6
31.555 -> 31.55
31.5555 -> 31.555
31.55555 -> 31.5556



32.55 -> 32.5
32.555 -> 32.55
32.5555 -> 32.556
32.55555 -> 32.5555



33.55 -> 33.5
33.555 -> 33.55
33.5555 -> 33.556
33.55555 -> 33.5555

.........

62.55 -> 62.5
62.555 -> 62.55
62.5555 -> 62.556
62.55555 -> 62.5555



63.55 -> 63.5
63.555 -> 63.55
63.5555 -> 63.556
63.55555 -> 63.5555



64.55 -> 64.5
64.555 -> 64.56
64.5555 -> 64.555
64.55555 -> 64.5555



65.55 -> 65.5
65.555 -> 65.56
65.5555 -> 65.555
65.55555 -> 65.5555



66.55 -> 66.5
66.555 -> 66.56
66.5555 -> 66.555
66.55555 -> 66.5555

There are the same result in JDK 1.7/1.8

Is it a bug of JDK?

like image 691
Liu guanghua Avatar asked Mar 12 '23 18:03

Liu guanghua


2 Answers

It's part of life sadly, and due to the fact that double types are implemented internally with a radix of 2. For scientific programming this is a remarkably clever thing to do. But it does mean that when you want to view or specify them with a base 10 radix, what you see is not necessarily what you get.

For example, the literal 10.555 is actually a number slightly less than this (10.5549999999999997157829056959599256515502929687...) So apparent German rounding will actually round this number downwards. Your question contains many similar cases.

This is not a bug as such, but if you need precise rounding then you ought to use a class equipped to represent decimal numbers with arbitrary precision.

Using BigDecimal is an option but you are misusing it horribly!. By using the constructor from a double (i.e. new BigDecimal(d)) the imprecision endemic in d will be merely copied to the BigDecimal. So you ought to use the constructor from a String instead, or some similar approach.

Finally, take a look at http://www.exploringbinary.com/floating-point-converter. Type in your decimal number and see the value that it assumes when converted to floating point.

like image 58
Bathsheba Avatar answered Mar 19 '23 13:03

Bathsheba


Use BigDecimal b = BigDecimal.valueOf(d).setScale(scale, roundingMode); instead.

As the docs say "This is generally the preferred way to convert a double (or float) into a BigDecimal, as the value returned is equal to that resulting from constructing a BigDecimal from the result of using Double.toString(double)."

like image 33
Tobias Avatar answered Mar 19 '23 13:03

Tobias