BigDecimal Subtraction - java

I want to substract 2 double values, and I have tried the following code.
double val1 = 2.0;
double val2 = 1.10;
System.out.println(val1 - val2);
and I got the output as,
0.8999999999999999
For getting output as 0.9 I tried with BigDecimal as follows,
BigDecimal val1BD = new BigDecimal(val1);
BigDecimal val2BD = new BigDecimal(val2);
System.out.println(val1BD.subtract(val2BD));
And I got the output as,
0.899999999999999911182158029987476766109466552734375
Then I tried with BigDecimal.valueOf()
val1BD = BigDecimal.valueOf(val1);
val2BD = BigDecimal.valueOf(val2);
System.out.println(val1BD.subtract(val2BD));
And finally I got the output as 0.9.
My question is what is the difference between case 2 & case 3?
In case 2 why I got the output like that?

BigDecimal.valueOf(double d) uses canonical String representation of double value, internally Double.toString(double) is used, that's why you are getting 0.9 in second case.
Note: This is generally the preferred way to convert a double (or
float) into a BigDecimal, as the value returned is equal to that
resulting from constructing a BigDecimal from the result of using
Double.toString(double).
While with new BigDecimal(0.9) it converts value to exact floating point representation of double value without using String representation,
Translates a double into a BigDecimal which is the exact decimal
representation of the double's binary floating-point value.
...
NOTES :
The results of this constructor can be somewhat unpredictable.
...
FOR EXAMPLE :
BigDecimal bd1 = new BigDecimal(Double.toString(0.9));
BigDecimal bd2 = new BigDecimal(0.9);
System.out.println(bd1);
System.out.println(bd2);
OUTPUT :
0.9
0.90000000000000002220446049250313080847263336181640625

Just for those others that got here looking for some other issue with BigDecimal(not related to the question above)...
remember to give a mathContext to the methods to avoid certain problems e.g.
MathContext mc = new MathContext(10, RoundingMode.HALF_UP);
BigDecimal hitRate = new BigDecimal(totalGetValuesHitted).divide(new BigDecimal(totalGetValuesRequested), mc);
BigDecimal missRate = new BigDecimal(1.0, mc).subtract(hitRate, mc);

Related

Is there a way to sum up Decimal values in java

I've been trying to sum up decimal values using double in java and it doesn't work well, got a wrong answer.
I've already tried with Double, Float, BigDecimal.
{
double a = 2595.00;
double b = -1760.76;
double c = -834.00;
double d = -.24;
System.out.print(a+b+c+d);
}
The expected result should be "0" But Got 9.1038288019262836314737796783447265625E-15
You can use BigDecimal for this purpose and make sure you input the numbers as String in the BigDecimal constructor:
BigDecimal a = new BigDecimal("2595.00");
BigDecimal b = new BigDecimal("-1760.76");
BigDecimal c = new BigDecimal("-834.00");
BigDecimal d = new BigDecimal("-.24");
System.out.println(a.add(b).add(c).add(d));
Live Example
Output is:
0.00
From the Java docs for 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.
Check this SO thread for why double results in a loss of precision.
As already pointed by the previous answers about double precision, the value here is very close to zero. You can see it with System.out.format as well.
System.out.format("%.14f%n",a+b+c+d);
System.out.format("%1.1f%n",a+b+c+d); //to print 0.0

BigDecimal Rounding incorrectly

I was expecting this code:
double valore = 20.775;
BigDecimal c = new BigDecimal(valore);
c = c.setScale(2, RoundingMode.HALF_UP);
System.out.println(c.doubleValue());
to return 20.78, but it's returning 20.77 instead.
Is it an error? Or am I missing something?
Everything is correct. You can read some high-level details in another answer on SO or read more in documentation to BigDecimal
It is not common to use BigDecimal constructor with double param, because it will exactly represent what is inside double.
So when you write: new BigDecimal(20.775) you are not necessarily have 20.775 as a result (rather you will have something like 20.77499999999999857891452847979962825775146484375)
For you to test:
1) testing BigDecimal representation
a) System.out.println(new BigDecimal(20.775)); => 20.77499999999999857891452847979962825775146484375
b) System.out.println(new BigDecimal("20.775")); => 20.775
2) test rounding with different BigDecimal constructors:
a) new BigDecimal(20.775) after rounding half up will show 20.77.
b) new BigDecimal("20.775") after rounding half up will show 20.78.
c) new BigDecimal(String.valueOf(20.775) after rounding half up will show 20.78.
So as a conclusion:
Do not use BigDecimal constructor with double param.
Instead use BigDecimal constructor with String param.
Hope it helps

Decimal Format Rounding Mode produces inconsistent values

I've found that using the decimal format and rounding mode shown behaves unexpectedly with some values
double b = 123.135;
//double b = 1896.675;
//double b = 523.135;
DecimalFormat df = new DecimalFormat(".##");
df.setRoundingMode(RoundingMode.HALF_UP);
System.out.println(b);
String a = df.format(b);
System.out.println(a);
double roundOff = Math.round(b * 100.0) / 100.0;
System.out.println(roundOff);
Produces:
123.135
123.14
123.14
Which I believe to be correct.
While using this value: 1896.675 produces the following:
1896.675
1896.67
1896.68
Which I regard as unexpected - What am I doing wrong here?
Problem here is that it is not possible to store all possible fractions in a variable because of the limitations of the binary format. So basically double just approximates the value you entered and thats why rounding errors occure.
You can read more about this topic in for example this thread: how does java.math.RoundingMode work?
Long story short, if you want precise rounding use BigDecimal (and the valueOf Function of the BigDecimal class)

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.
[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.
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
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);
You have to get the return value:
BigDecimal b3 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b3);
BigDecimal(double val)
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 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 does b1.toString() evaluate to "0E-59" and not to something like "0.0", "0E0" or just "0"?
The reason is that toString() prints the canonical format of the BigDecimal. See BigDecimal.toString() for more information.
At the end, 0E-59 is 0.0 - it is 0*10^59 which mathematically evaluates to 0. So, the unexpected result is a matter of the internal representation of the BigDecimal.
To get the float or double values, use
b1.floatValue());
or
b1.doubleValue());
Both evaluate to 0.0.
It's a known issue, BigDecimal(double val) API The results of this constructor can be somewhat unpredictable. Though it looks really wierd in this interpertation. Actual reason is that new BigDecimal(0.01) produces a BigDecimal with approx values
0.01000000000000000020816681711721685132943093776702880859375
which has a long precision, and so the result of subtract has a long precision too.
Anyway, we can solves the "problem" this way
BigDecimal b1 = new BigDecimal("0.01");
BigDecimal b2 = new BigDecimal("0.01");
or we can use a constructor with setting a precision
BigDecimal b1 = new BigDecimal(0.01, new MathContext(1));
BigDecimal b2 = new BigDecimal(0.01, new MathContext(1));
Use like this:
BigDecimal b1 = BigDecimal.valueOf(0.01);
BigDecimal b2 = BigDecimal.valueOf(0.01);
b1 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b1);

"new BigDecimal(13.3D)" results in imprecise "13.3000000000000007105.."?

How is it that Java's BigDecimal can be this painful?
Double d = 13.3D;
BigDecimal bd1 = new BigDecimal(d);
BigDecimal bd2 = new BigDecimal(String.valueOf(d));
System.out.println("RESULT 1: "+bd1.toString());
System.out.println("RESULT 2: "+bd2.toString());
RESULT 1: 13.300000000000000710542735760100185871124267578125
RESULT 2: 13.3
Is there any situation where Result 1 would be desired? I know that Java 1.5 changed the toString() method but was this the intended consequence?
Also I realise that BigDecimal has doubleValue() etc, but the library that I am working with helpfully uses a toString() and I can't change that :-(
Cheers.
Well, the API does address this apparent inconsistency in the constructor BigDecimal(double val):
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.
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.
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.
Moral of the story: The pain seems self-inflicted, just use new BigDecimal(String val) or BigDecimal.valueOf(double val) instead =)
Your problem has nothing to do with BigDecimal, and everything with Double, which cannot represent 13.3 accurately, since it uses binary fractions internally.
So your error is introduced in the very first line. The first BigDecimal simply preserves it, while String.valueOf() does some fishy rounding that causes the second one to have the desired content, pretty much through luck.
You might want to inform yourself about how floating-point values are implemented (IEEE 754-1985). And suddenly, everything will become crystal-clear.
This isn't the fault of BigDecimal - it's the fault of double. BigDecimal is accurately representing the exact value of d. String.valueOf is only showing the result to a few decimal places.
Fractions represented with binary number types(i.e. double, float) cannot be accurately stored in those types.
Double d = 13.3;
BigDecimal bdNotOk = new BigDecimal(d);
System.out.println("not ok: " + bdNotOk.toString());
BigDecimal bdNotOk2 = new BigDecimal(13.3);
System.out.println("not ok2: " + bdNotOk2.toString());
double x = 13.3;
BigDecimal ok = BigDecimal.valueOf(x);
System.out.println("ok: " + ok.toString());
double y = 13.3;
// pretty lame, constructor's behavior is different from valueOf static method
BigDecimal bdNotOk3 = new BigDecimal(y);
System.out.println("not ok3: " + bdNotOk3.toString());
BigDecimal ok2 = new BigDecimal("13.3");
System.out.println("ok2: " + ok2.toString());
Double e = 0.0;
for(int i = 0; i < 10; ++i) e = e + 0.1; // some fractions cannot be accurately represented with binary
System.out.println("not ok4: " + e.toString()); // should be 1
BigDecimal notOk5 = BigDecimal.valueOf(e);
System.out.println("not ok5: " + notOk5.toString()); // should be 1
/*
* here are some fractions that can be represented exactly in binary:
* 0.5 = 0.1 = 1 / 2
* 0.25 = 0.01 = 1 / 4
* 0.75 = 0.11 = 3 / 4
* 0.125 = 0.001 = 1 / 8
*/
output:
not ok: 13.300000000000000710542735760100185871124267578125
not ok2: 13.300000000000000710542735760100185871124267578125
ok: 13.3
not ok3: 13.300000000000000710542735760100185871124267578125
ok2: 13.3
not ok4: 0.9999999999999999
not ok5: 0.9999999999999999
Just use BigDecimal.valueOf(d) or new BigDecimal(s).

Categories