user1514499 user1514499 - 1 month ago 11
Java Question

java BigDecimal subraction failing

I tried the following code. but getting different result when subtracting using BigDecimal.

double d1 = 0.1;
double d2 = 0.1;
System.out.println("double result: "+ (d2-d1));

float f1 = 0.1F;
float f2 = 0.1F;
System.out.println("float result: "+ (f2-f1));

BigDecimal b1 = new BigDecimal(0.01);
BigDecimal b2 = new BigDecimal(0.01);

b1 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b1);


Result:

double result: 0.0
float result: 0.0
BigDecimal result: 0E-59


I am still working on this. can anyone please clarify.

Answer

Interesting, the values appear to be equal and subtraction does give you zero, it appears to just be an issue with the printing code. The following code:

import java.math.BigDecimal;
public class Test {
    public static void main(String args[]) {
        BigDecimal b1 = new BigDecimal(0.01);
        BigDecimal b2 = new BigDecimal(0.01);
        BigDecimal b3 = new BigDecimal(0);
        if (b1.compareTo(b2) == 0) System.out.println("equal 1");
        b1 = b1.subtract(b2);
        if (b1.compareTo(b3) == 0) System.out.println("equal 2");
        System.out.println("BigDecimal result: "+ b1);
    }                          
}

outputs both equal messages, indicating that the values are the same and that you get zero when you subtract.

You could try to raise this as a bug and see what Oracle comes back with. It's likely they'll just state that 0e-59 is still zero, so not a bug, or that the rather complex behaviour being described on the BigDecimal documentation page is working as intended. Specifically, the point that states:

There is a one-to-one mapping between the distinguishable BigDecimal values and the result of this conversion. That is, every distinguishable BigDecimal value (unscaled value and scale) has a unique string representation as a result of using toString. If that string representation is converted back to a BigDecimal using the BigDecimal(String) constructor, then the original value will be recovered.

That fact that the original value needs to be recoverable means that toString() needs to generate a unique string for each scale, which is why you're getting 0e-59. Otherwise, converting the string back to a BigDecimal may give you a different value (unscaled-value/scale tuple).

If you really want zero to show up as "0" regardless of the scale, you can use something like:

if (b1.compareTo(BigDecimal.ZERO) == 0) b1 = new BigDecimal(0);
Comments