Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 693
user1514499 Avatar asked Mar 15 '13 09:03

user1514499


People also ask

Can BigDecimal be null in Java?

You either never allow null values in database, application or view and initialize everything with new BigDecimal(0) or perform null checks on every usage for nullable values.

How do I know if BigDecimal is 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.

How do you subtract two BigDecimals?

subtract(BigDecimal subtrahend, MathContext mc) returns a BigDecimal whose value is (this - subtrahend), with rounding according to the context settings. If subtrahend is zero then this, rounded if necessary, is used as the result. If this is zero then the result is subtrahend. negate(mc).


2 Answers

[There are a lot of answers here telling you that binary floating-point can't exactly represent 0.01, and implying that the result you're seeing is somehow inexact. Whilst the first part of that is true, it's not really the core issue here.]


The answer is that "0E-59" is equal to 0. Recall that a BigDecimal is the combination of an unscaled value and a decimal scale factor:

System.out.println(b1.unscaledValue());
System.out.println(b1.scale());

displays:

0
59

The unscaled value is 0, as expected. The "strange" scale value is simply an artifact of the decimal expansion of the non-exact floating-point representation of 0.01:

System.out.println(b2.unscaledValue());
System.out.println(b2.scale());

displays:

1000000000000000020816681711721685132943093776702880859375
59

The next obvious question is, why doesn't BigDecimal.toString just display b1 as "0", for convenience? The answer is that the string representation needs to be unambiguous. From the Javadoc for toString:

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.

If it just displayed "0", then you wouldn't be able to get back to this exact BigDecimal object.

like image 112
Oliver Charlesworth Avatar answered Sep 23 '22 22:09

Oliver Charlesworth


Use constructor from String: b1 = new BigDecimal("0.01");

Java loss of precision

(slide 23) http://strangeloop2010.com/system/talks/presentations/000/014/450/BlochLee-JavaPuzzlers.pdf

like image 21
Ivan Borisov Avatar answered Sep 26 '22 22:09

Ivan Borisov