1

I'm using the DecimalFormat with HALF_UP rounding mode and I have an escenery where is not working correctly and I don't know why.

DecimalFormat df = new DecimalFormat("#.##");
df.setRoundingMode(RoundingMode.HALF_UP);
float tmp = (float) (0.5 * 1.05);
df.format(tmp);
float mul = Float.parseFloat(df.format(tmp));

The mul variable value I hope have 0.53 value and I received 0.52 value.

I'm using the Java 1.8.0_131.

SOLVED FINAL CODE

BigDecimal mul = new BigDecimal(0.5).multiply(new igDecimal(1.05));
mul = mul.setScale(2, RoundingMode.HALF_UP);
System.out.println(mul);
j.barrio
  • 1,006
  • 1
  • 13
  • 37
  • Thank you for your comment, but I need rounding as HALF_UP must works "Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up." [Oracle Documentation](https://docs.oracle.com/javase/7/docs/api/java/math/RoundingMode.html) – j.barrio Jul 14 '17 at 07:18
  • 2
    `float` and `double` cannot store values precisely... See: https://stackoverflow.com/q/3413448/4899193 – Usagi Miyamoto Jul 14 '17 at 07:22
  • this works `System.out.println(df.format(0.5 * 1.05));` - of course doubles – Scary Wombat Jul 14 '17 at 07:28
  • I do not think.... Try printing out `tmp`, like `System.out.println(""+tmp);` – Usagi Miyamoto Jul 14 '17 at 07:29
  • As per the answer below, `double` is sufficient, while `float` is not. And `tmp` is declare as `float`.... – Usagi Miyamoto Jul 14 '17 at 07:31

1 Answers1

9

You are using the float datatype.

This datatype is not able to precisely hold the value 0.525. See this code for making it clear:

float value = (float) (0.5 * 1.05);
DecimalFormat df = new DecimalFormat("#.########################");
System.out.println(df.format(value));

This prints out:

0.5249999761581421

Rounding such a value with the mode RoundingMode.HALF_UP will correctly yield 0.52.

The double value seems to be able to precisely store the value 0.525:

double value = 0.5 * 1.05;
DecimalFormat df = new DecimalFormat("#.########################");
System.out.println(df.format(value));

This will print the expected value:

0.525

Rounding that value with the mode RoundingMode.HALF_UP will now yield 0.53!

Caution: Even the double datatype does not store the value precisely!

Look at @MarkDickinson's comment. The stored value is 0.52500000000000002220446049250313080847263336181640625 which happens to be larger than 0.525 and only rounds by accident to the expected value.

So what to do?

The data types float and double are binary-based, whereas we humans tend to think decimal-based when dealing with numbers. Read the article "What Every Computer Scientist Should Know About Floating-Point Arithmetic" for much more information.

The solution is to use a decimal-based data type, which exists in BigDecimal.

Seelenvirtuose
  • 20,273
  • 6
  • 37
  • 66
  • 1
    This answer is misleading; a `double` is _not_ able to store the value `0.525` precisely: it stores `0.52500000000000002220446049250313080847263336181640625` instead. Because that value just _happens_ to be above `0.525`, it rounds upwards. Try with `0.7 * 1.05` and you'll see it rounding down instead of up. If the OP cares about correct rounding of _decimal_ halfway cases, they need to be using a decimal-based numeric type. – Mark Dickinson Jul 14 '17 at 07:43
  • Thank you for your quickly answer, but my rounding continue with something wrong: `double tmp = 0.5 * 1.05; DecimalFormat df = new DecimalFormat("#.##"); df.setRoundingMode(RoundingMode.HALF_UP); System.out.println(df.format(tmp));` Thats returns 1.52 – j.barrio Jul 14 '17 at 07:48
  • @MarkDickinson Thanks for your finding. I somehow missed it. Changed my answer. – Seelenvirtuose Jul 14 '17 at 08:01