Java BigDecimal Error

I tried the following code. but getting different results when subtracting with 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 it. can anyone clarify.

+7
source share
8 answers

Interestingly, the values ​​seem to be equal, and subtraction gives you a null value, it seems to be a problem with the print 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); } } 

displays both equal messages, indicating that the values match, and when subtracting you get zero .

You can try to raise this as an error and see what Oracle returns with. They'll probably just indicate that 0e-59 is still zero, so it’s not an error, or that the rather complicated behavior described on the documentation page of BigDecimal works as intended. In particular, the point that reads:

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

The fact that the original value needs to be restored means that toString() needs to create a unique row for each scale, so you get 0e-59 . Otherwise, converting the string back to BigDecimal may give you a different value (unscaled-value / scale tuple).

If you really want the zero to display as β€œ0” regardless of scale, you can use something like:

 if (b1.compareTo(BigDecimal.ZERO) == 0) b1 = new BigDecimal(0); 
+4
source

[There are many answers that state that a binary floating point cannot represent exactly 0.01, and implying that the result you see is somehow inaccurate. Although the first part of this right is true, this is not really the main problem.]


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

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

displayed:

 0 59 

The unscaled value is 0, as expected. The strange value of the scale is simply an artifact of the decimal decomposition of an inaccurate floating point representation of 0.01:

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

displayed:

 1000000000000000020816681711721685132943093776702880859375 59 

The next obvious question is: why does BigDecimal.toString just display b1 as " 0 " for convenience? The answer is that the string representation should be unambiguous. From Javadoc for toString :

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

If it just displays " 0 ", you cannot return to this exact BigDecimal object.

+11
source
+7
source

You should get the return value:

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

So the real question is: with the following code,

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

why is b1.toString() evaluated as "0E-59" and not something like "0.0" , "0E0" or just "0" ?


The reason is that toString() prints the canonical BigDecimal format. See BigDecimal.toString () for more details.

In the end, 0E-59 - 0.0 is 0*10^59 , which mathematically evaluates to 0. Thus, the unexpected result is an internal representation of BigDecimal .

To get float or double values, use

 b1.floatValue()); 

or

 b1.doubleValue()); 

Both are evaluated to 0.0 .

+2
source

BigDecimal (double val)

1. The results of this constructor may be somewhat unpredictable. We can assume that writing a new BigDecimal (0.1) in Java creates a BigDecimal, which is exactly 0.1 (unscaled value 1, with a scale of 1), but in fact it is 0.1000000000000000055511151231257827021181583404541015625. This is due to the fact that 0.1 cannot be represented exactly as double (or, for this matter, as a binary fraction of any finite length). Thus, the value that is passed to the constructor is not exactly 0.1, despite this.

2. The String constructor, on the other hand, is quite predictable: writing a new BigDecimal ("0.1") creates a BigDecimal that is exactly 0.1, as you would expect. Therefore, it is generally recommended that you use the String constructor for this one.

3. When double should be used as the source for BigDecimal, note that this constructor provides accurate conversion; it does not give the same result as converting double to string using Double.toString (double) and then using the BigDecimal (String) constructor. To get this result, use the static value of the Of (double) Method.

+2
source

This is a known issue, the BigDecimal (double val) API. The results of this constructor may be somewhat unpredictable. Although it looks really weird at this break. The actual reason is that the new BigDecimal (0.01) creates a BigDecimal with approximate values

 0.01000000000000000020816681711721685132943093776702880859375 

which has greater accuracy, so the result of the subtraction also has greater accuracy.

In any case, we can solve this problem.

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

or we can use a precision constructor

 BigDecimal b1 = new BigDecimal(0.01, new MathContext(1)); BigDecimal b2 = new BigDecimal(0.01, new MathContext(1)); 
+1
source

Use this:

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

All Articles