3

Two decimal places are kept, the fractional part is the same, and the result is inconsistent

jdk1.8.0_162

DecimalFormat df = new DecimalFormat("##.00");
df.setRoundingMode(RoundingMode.HALF_UP);
System.out.println("1.985 ≈ " + df.format(1.985));
System.out.println("23.985 ≈ " + df.format(23.985));
1.985 ≈ 1.99
23.985 ≈ 23.98

The output is as above, and should be the same as the fractional part.

Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
Jason
  • 31
  • 2

2 Answers2

4

This is how floating point types work. They can be an approximation. The approximations for your two numbers are not the same since they are not the same numbers. The one number might be 1.985000000000001 and the other number might be 23.98499999999999 internally. See also here.

cmoetzing
  • 742
  • 3
  • 16
  • 1
    The floating point number 0.5 is not an approximation. – Roland Illig Apr 12 '19 at 06:18
  • @RolandIllig I disagree. What would be your explanation if I might ask? – cmoetzing Apr 12 '19 at 06:31
  • 3
    @cmoetzing Floatings are not approximations, they are well defined exact values... **BUT** they are used to store approximations of rational values. – Jean-Baptiste Yunès Apr 12 '19 at 08:22
  • @Jean-BaptisteYunès In the context of this question: if they are exact values of 23.985 and 1.985 why are they rounded to different numbers? My explanation: Java stores an approximation of 1.985, not exactly 1.985. – cmoetzing Apr 12 '19 at 09:39
  • I get your argument but I think it depends on the point of view. As a programmer you tell Java to use one value and it will use the one closest to that value if it can not be represented. Sounds like an approximation, doesn't it? – cmoetzing Apr 12 '19 at 09:48
  • 1
    @cmoetzing float literals are not floats, floats literals are converted to floats. They are not in the same domain. The sentence "they can be an approximation" is misleading. Floats are well defined values. Literals are approximated to floats values. Approximation here is a process, not a value. – Jean-Baptiste Yunès Apr 12 '19 at 09:48
0

The issue you are seeing comes from specifying "23.985" in code, which when represented as a double is something slightly less.

A possible solution (if you absolutely need it to round the right way) is to use BigDecimal.valueOf to create the value. For example

//BigDecimal bd = BigDecimal.valueOf(23.985);
BigDecimal bd = new BigDecimal("23.985");
System.out.println("bd=" + bd);     
System.out.println(df.format(bd));  // expected == actual
Vishwa Ratna
  • 5,567
  • 5
  • 33
  • 55
  • You'd get the same problem with this, because it still uses the double. You have to use `BigDecimal.valueOf("23.985")`. – Andy Turner Apr 12 '19 at 06:26
  • @AndyTurner , isnt this line `BigDecimal bd = BigDecimal.valueOf(23.985); `equivalent ? – Vishwa Ratna Apr 12 '19 at 06:28
  • Would you say your 'is something slightly less.' is what I tried to express in my answer or am I wrong like @RolandIllig is suggesting? – cmoetzing Apr 12 '19 at 06:29
  • @CommonMan no. `23.985` is the representable double value closest to 23.985, which is not exactly the same, and then you construct a `BigDecimal` which exactly represents that "incorrect" value. – Andy Turner Apr 12 '19 at 06:30
  • Sorry, it's `new BigDecimal(String)`, not `valueOf(String)`. – Andy Turner Apr 12 '19 at 06:33
  • Andy, i still did not understand the conflict betwen `BigDecimal bd = new BigDecimal("23.985");` and `BigDecimal bd = BigDecimal.valueOf(23.985);` , wont both return a new object with value?? – Vishwa Ratna Apr 12 '19 at 06:36
  • 2
    Well, if your goal only is to “return a new object with value”, then both do it. But if your goal is to construct a `BigDecimal` representing the *exact value* `23.985`, you have to specify the value with a `String`, as specifying a `double` value has the very problem you’re describing in your answer, “which when represented as a double is something slightly less”. – Holger Apr 12 '19 at 06:43