Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BigDecimal compareTo not working as expected

According to the JavaDoc for BigDecimal, the compareTo function does not account for the scale during comparison.

Now I have a test case that looks something like this:

BigDecimal result = callSomeService(foo);
assertTrue(result.compareTo(new BigDecimal(0.7)) == 0); //this does not work
assertTrue(result.equals(new BigDecimal(0.7).setScale(10, BigDecimal.ROUND_HALF_UP))); //this works

The value I'm expecting the function to return is 0.7 and has a scale of 10. Printing the value shows me the expected result. But the compareTo() function doesn't seem to be working the way I think it should.

What's going on here?

like image 367
Akshay Avatar asked May 19 '11 12:05

Akshay


People also ask

How accurate is BigDecimal?

BigDecimal precision is de facto unlimited since it is based on an int array of arbitrary length. Though operations with double are much faster than with BigDecimal this data type should never be used for precise values, such as currency.

How do you know if two BigDecimal values are equal?

equals() method checks for equality of a BigDecimal value with the object passed. This method considers two BigDecimal objects equal if only if they are equal in value and scale.

What can I use instead of BigDecimal?

If you need to use division in your arithmetic, you need to use double instead of BigDecimal.

How does BigDecimal value compare with zero?

Using the compareTo Method Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. Therefore, we can check BigDecimal. ZERO. compareTo(givenBdNumber) == 0 to decide if givenBdNumber has the value zero.


1 Answers

new BigDecimal(0.7) does not represent 0.7.

It represents 0.6999999999999999555910790149937383830547332763671875 (exactly).

The reason for this is that the double literal 0.7 doesn't represent 0.7 exactly.

If you need precise BigDecimal values, you must use the String constructor (actually all constructors that don't take double values will work).

Try new BigDecimal("0.7") instead.

The JavaDoc of the BigDecimal(double) constructor has some related 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.

  3. When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

So to summarize: If you want to create a BigDecimal with a fixed decimal value, use the String constructor. If you already have a double value, then BigDecimal.valueOf(double) will provide a more intuitive behaviour than using new BigDecimal(double).

like image 198
Joachim Sauer Avatar answered Oct 14 '22 14:10

Joachim Sauer