Edmund Chee
Edmund Chee

Reputation: 43

Is there a precision matter when comparing two double variables that are rounded down to specific decimals?

I am trying to compare two double type variables that are rounded down to a specific decimal place using BigDecimals and RoundingMode.

For instance, when comparing 3.1456 and 3.145, rounded down to 3 decimal places in an if statement, my if statement should return true only if both values are equivalent to one another.

However, it did not work for my code

BigDecimal one = new BigDecimal(3.1456).setScale(3, RoundingMode.DOWN);
BigDecimal two = new BigDecimal(3.145).setScale(3, RoundingMode.DOWN);

if (one.equals(two)) {
    System.out.println("Both variables are equal");
} else {
    System.out.println("Both variables are not equal");
}

The output logic was false. So I did some checking to see the values that my variables are rounded down to. It was 3.145, and 3.144 respectively which was the unexpected one. Can't find out the reason why it's 3.144...

Next, I used another method to do the above by using DecimalFormat and RoundingMode. It worked pretty well.

DecimalFormat df0 = new DecimalFormat("#.###");
DecimalFormat df1 = new DecimalFormat("#.###");
df0.setRoundingMode(RoundingMode.DOWN);
df1.setRoundingMode(RoundingMode.DOWN);

if (df0.format(num0).equals(df1.format(num1))) {
    System.out.println("Both variables are equal")
} else {
    System.out.println("Both variables are not equal")
}

The method above works very well! BigDecimal evaluates both values to 3.145.

I just can't understand why it doesn't work when I use BigDecimal to do the task, compared to when I use DecimalFormat. Is there something to do with how double type variables work? Something like (double)(0.2-0.1) which gives a result of 0.09999999... and not 0.1?

Please enlighten me with a better solution and understanding! Thank you so much!

Upvotes: 2

Views: 118

Answers (1)

Vince
Vince

Reputation: 15146

As documented, the BigDecimal(double) is unpredictable:

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.


They suggest using BigDecimal(String) as an alternative:

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.

From the docs of BigDecimal(String):

This is generally the preferred way to convert a float or double into a BigDecimal, as it doesn't suffer from the unpredictability of the BigDecimal(double) constructor.

Upvotes: 1

Related Questions