2

I was searching for a precise way to multiply two floating point numbers in Java, and I read that I should use BigDecimal, however it doesn't work as expected. What am I doing wrong?

My code:

BigDecimal a = new BigDecimal(3.53);
BigDecimal b = new BigDecimal(3.59);
BigDecimal c = a.multiply(b);

System.out.println(c);

Result:

12.672699999999998796873512674210388041622697702955394242845497954075284496866515837609767913818359375

Expected result:

12.6727

1 Answers1

13

When you use a BigDecimal(double) constructor it cannot be more precise than a double, use the String form instead. Like,

BigDecimal a = new BigDecimal("3.53");
BigDecimal b = new BigDecimal("3.59");
BigDecimal c = a.multiply(b);
System.out.println(c);

Which outputs

12.6727

The linked Javadoc says in part -

Notes:

  1. The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.

  2. The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.

Community
  • 1
  • 1
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • How about using "BigDecimal a = BigDecimal.valueOf(3.53d);" ? That way I can use an actual double variable instead of converting it to String, and still retain precision – Michał Terczyński Aug 13 '17 at 18:20
  • @Mikemike that's still a bad idea. Using doubles in the first place is the problem. Use String and BigDecimal without ever touching doubles in between. If you use that approach with a number too precise for double, you'll still get wrong results. – Louis Wasserman Aug 13 '17 at 19:12
  • @Louis Wasserman Somehow I got right results: BigDecimal a = BigDecimal.valueOf(3.53d); BigDecimal b = BigDecimal.valueOf(3.59d); BigDecimal c = a.multiply(b); System.out.println(c); // 12.6727 – Michał Terczyński Aug 13 '17 at 20:34
  • I was very specific: a number too precise for double. It won't go wrong soon, but it can go wrong if you have enough digits, and it's still a bad idea. – Louis Wasserman Aug 13 '17 at 20:36
  • 3
    @Mikemike: Do not **convert** a double to a string. That would still have a big chance of getting the wrong values. **Start** right away with strings. Do not use doubles at all. If you use double, you may be lucky and get a reasonable result. But chances are you are not lucky. Double can represent some decimal values exactly, but not most of them. – Rudy Velthuis Aug 14 '17 at 11:07