Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiplying two numbers using BigDecimal returns a wrong value

Executing the following code:

new BigDecimal(0.06 * 3).toString() 

returns 0.179999999999999993338661852249060757458209991455078125 instead of 0.18.

Executing

new BigDecimal(0.06).multiply(new BigDecimal(3)).toString()  

returns the same result.

How is this possible?

like image 475
Mark Avatar asked Aug 07 '14 09:08

Mark


People also ask

How does BigDecimal value multiply?

Use the multiply() method to multiply one BigDecimal to another in Java. This method returns a BigDecimal whose value is (this × multiplicand), and whose scale is (this. scale() + multiplicand.

How accurate is BigDecimal?

This limits it to 15 to 17 decimal digits of accuracy. BigDecimal can grow to any size you need it to. Double operates in binary which means it can only precisely represent numbers which can be expressed as a finite number in binary. For example, 0.375 in binary is exactly 0.011.

How do you multiply INT with BigDecimal?

multiply() returns a BigDecimal and you're trying to store that in an int . Second, it takes another BigDecimal as the argument, not an int . If you just use the BigDecimal for all variables involved in these calculations, it should work fine.

Which is more accurate BigDecimal or double?

A BigDecimal is an accurate way of expressing numbers. A Double has a reliable accuracy. Going with doubles of various magnitudes (say d1=1000.0 and d2=0.001) could occur in the 0.001 being dropped collectively when summing as the variation in magnitude is so large. With BigDecimal this would not occur.


2 Answers

You're not multiplying two numbers using BigDecimal. You're multiplying them using double arithmetic, and passing the result to the BigDecimal constructor.

You want:

new BigDecimal("0.06").multiply(new BigDecimal("3")).toString() 

Note that you do want the values in strings - otherwise you're using the double value for 0.06, which isn't exactly 0.06... you've lost information before you start. (You don't really need the string form of 3, but I've done so for consistency.)

For example:

System.out.println(new BigDecimal(0.06)); 

prints

0.059999999999999997779553950749686919152736663818359375 
like image 195
Jon Skeet Avatar answered Oct 02 '22 07:10

Jon Skeet


As Jon Skeet writes above, the reason why you are getting 0.179999999999999993338661852249060757458209991455078125 instead of 0.18 is because 0.06 * 3 is computed as an IEEE 754 double and then this double value is converted to a BigDecimal.

Even though 0.06 looks simple enough in source code, the number 0.06 is not exactly representable as an IEEE 754 double, so what 0.06 actually represents is an approximation of 0.06 equal to 0.059999999999999997779553950749686919152736663818359375. The number 0.06 in decimal notation is not exactly representable because the number equals 0b0.000011110101110000101 in binary notation (where bold represents a repeating digit sequence). The computer must truncate this infinite sequence of binary digits, leading to the 0.0599999... approximation.

As I detailed in my answer to Question regarding IEEE 754, 64 bits double?, you can use ARIBAS' decode_float() function to determine the mantissa and exponent of a floating-point number:

 ==> set_floatprec(double_float). -: 64  ==> set_printbase(2). -: 0y10  ==> decode_float(0.06). -: (0y11110101_11000010_10001111_01011100_00101000_11110101_11000010_10001111,  -0y1000100)  ==> set_printbase(10). -: 10  ==> -0y1000100. -: -68  ==> set_floatprec(128). -: 128  ==> 1/2**4 + 1/2**5 + 1/2**6 + 1/2**7 + 1/2**9 + 1/2**11 + 1/2**12 + 1/2**13 + 1/2**18 + 1/2**20. -: 0.11999_98855_59082_03125_00000_00000_00000_00 

(** is exponentiation in ARIBAS).

And we have 0.06 = Σ i = 0..∞0.11999988555908203125 / 21 + 20 × i

You can evaluate this series in a computer algebra system such as Maxima:

 (%i1) sum ( 0.11999988555908203125 / 2 ^ (1 + 20 * i), i, 0, inf ), simpsum; (%o1)                                0.06 

http://maxima-online.org/?inc=r760264757

like image 30
Daniel Trebbien Avatar answered Oct 02 '22 06:10

Daniel Trebbien