B. Abraham
B. Abraham

Reputation: 53

Java DecimalFormat rounding issue ( consistency problem)

I am seeing some strange behavior in trying to round doubles to 2 decimal places

Below is the code

        DecimalFormat df = new DecimalFormat();
        df.setMaximumFractionDigits(2);
        df.setRoundingMode(RoundingMode.HALF_EVEN);
        
        double f1 = 100.845;
                
        double f2 = 1440.845;
        
        System.out.println( df.format(f1));
        System.out.println( df.format(f2));

This outputs

100.84
1,440.85

I was expecting 100.85 for the first value.

As I was looking into it further, I found the following

101.845 --> 100.84
102.845 --> 102.84
103.845 --> 103.84
.
.
255.845 --> 255.84
**256.845 --> 256.85**
257.845 --> 256.85
.
.

I am thinking it must have something to do with the precision as the switch from .84 to .85 occurred around 255( 2^8 -1). But, I am not calculating this value( at least not in this sample code).

Can anyone shed some light on why this is happening?

Thank you

Upvotes: 0

Views: 114

Answers (2)

M. Justin
M. Justin

Reputation: 21094

The issue here is due to the fact that neither of these double values are exactly equal to the decimal representation used in the Java code; they're instead a close floating point value.

The following is one way to print their exact values:

System.out.println(new BigDecimal(100.845));
System.out.println(new BigDecimal(1440.845));

This outputs:

100.844999999999998863131622783839702606201171875
1440.845000000000027284841053187847137451171875

100.8449... rounds down to 100.84, since it's closest to 100.84. 1440.8450... rounds up to 100.85, since it's closest to 100.85.

In neither of these cases does the half-even logic apply, since neither number is exactly halfway between two potential rounding targets.

By contrast, if you use a number format that can exactly represent these values (such as BigDecimal), you'll see the expected rounding:

DecimalFormat df = new DecimalFormat();
df.setMaximumFractionDigits(2);
df.setRoundingMode(RoundingMode.HALF_EVEN);

BigDecimal d1 = new BigDecimal("100.845");
BigDecimal d2 = new BigDecimal("1440.845");

System.out.println( df.format(d1));
System.out.println( df.format(d2));

Output:

100.84
1,440.84

Upvotes: 3

Frederic Perron
Frederic Perron

Reputation: 798

When using HALF_EVEN, it rounds towards the "nearest neighbor" unless both neighbors are equidistant. If they are equidistant, it rounds towards the even neighbor to the left...

In you case, 100.845 the last digit 5 is equidistant so it rounds to the nearest even neighbor to the left 4.

Here, you are using double which, in memory, can be sligtly off with the value. To make sure the numbers that you are rouding are not equidistand, add the precision error which is + 1e-6 for your double.

Upvotes: 0

Related Questions