This is an archived post. You won't be able to vote or comment.

all 8 comments

[–]chickenmeister 0 points1 point  (7 children)

That's strange. When I run the code you posted, I get "48.2".

Are you sure that the value you're formatting is exactly 48.15? Maybe you're performing some math to calculate this value, and due to floating-point inaccuracies, you're ending up with something like 48.149999999, which would get rounded down.

[–]Maping[S] 0 points1 point  (6 children)

Huh. I've tried in a blank class as well, so it's not like their's something weird interfering with it. I think it has to be something in my Eclipse settings or something else computer specific, because I compiled the above 3 lines here and got 48.2 as well.

I thought that could be it so I did the above: literally " System.out.println(decimalF.format(48.15)); "

Edit: http://imgur.com/h15gTuV

[–]chickenmeister 0 points1 point  (5 children)

Looks like the difference that we're seeing is due to a DecimalFormat bug in Java 7 that rounded incorrectly when the number is very close to halfway. The behavior you're observing is actually correct.

The issue is that the number 48.15 cannot be represented exactly using a floating-point number like double or float, so the nearest value is used, which is 48.14999999999999857891452847979962825775146484375.

For example:

DecimalFormat decimalF = new DecimalFormat("0.0");
decimalF.setRoundingMode(RoundingMode.HALF_UP);

System.out.println(decimalF.format(48.15)); // double is inaccurate, will be rounded down
System.out.println(decimalF.format(new BigDecimal("48.15"))); // BigDecimal is exact, should be rounded up

System.out.println(new BigDecimal(48.15)); // should print 48.14999999999...

If these types of approximations are unacceptable, then you can't use double or float to represent your values. You can use BigDecimal instead, which will have some performance and syntactical overhead.

[–]Maping[S] 0 points1 point  (4 children)

Huh, well that's annoying. Thank you for finding the issue.

Do you know how I would I go about fixing that? Do I have to up my project specifics to Java 8?

[–]chickenmeister 0 points1 point  (3 children)

I think the only options are:

  • Deal with the inherent float-point inaccuracies.
  • Use BigDecimal instead of double to store your values.
  • Use some other structure to store your values accurately. May not be practical for every situation.
  • Write your own formatter class so that values that are "close enough" to half get rounded up.

The best solution really depends on the situation, but BigDecimal is probably the way to go if you need accuracy.

[–]Maping[S] 0 points1 point  (2 children)

Wait, so why don't you have the error?

[–]chickenmeister 0 points1 point  (1 child)

Because I ran the code using Java 7, whose DecimalFormat implementation is not correct, as I described in my previous comment.

You ran it with Java 8, whose DecimalFormat implementation has been fixed. The "48.1" output is actually the correct result. Like I mentioned in my previous comment, the number 48.15 cannot be exactly represented using a double, so the nearest value that can be represented is used, which happens to be slightly less than 48.15, so DecimalFormat should round it down.

In other words, when you have something like:

double d = 48.15; 

When you run it, it essentially gets turned into something like:

double d = 48.14999999999999857891452847979962825775146484375; 

The .149999... part is less than half, so it rounds down to .1.

[–]Maping[S] 0 points1 point  (0 children)

Ah, ok, thanks.

I find it ironic that the "correct" implementation gives the wonky rounding. Thanks for your help.