BigDecimal Rounding incorrectly - java

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

Related

How to properly calculate rational by using BigDecimal

How can I get the correct answer when I need to use BigDecimal without losing precision.
BigDecimal a = new BigDecimal(0.5);
BigDecimal b = new BigDecimal(30);
BigDecimal c = new BigDecimal(18000);
a.divide(b).multiply(c);
How could I get the exact 300 in this case?
Thanks!
You can use the MathContext parameter in the divide method for this.
For example, a.divide(b, MathContext.DECIMAL128).multiply(c); will give you the precision you need (with an error of magnitude 1e-32). If you do not want to lose any precision, you can use MathContext.UNLIMITED, but this will result in a non-terminating decimal expansion.
In your case specifically, you can also try to rewrite the equation to prevent any rounding from happening: a / b * c = c / b * a.

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 Subtraction

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);

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);

Android - exact mathematical calculation

I have got a Problem, I am developing an Application which should be able to do some mathematic calculations. These calculations have to be exact (or rather not obviously wrong)
But this simple Code
double a = 3.048d;
double b = 1000d;
double c = a / b;
gives me a wrong result c is not 0.003048 as expected instead it is 0.0030480000000000004 which is obviously wrong.
double d = 3.048 / 1000;
this second code-snipet gives the correct result.
I am aware that all floatingpoint arithmetic is not exact when calculating with computers but I don't know how to solve this problem.
thanks in advance!
Ludwig
Developing for:
- Android 2.2
Testdevice:
- HTC Desire
What you need to use for exact percision is the BigDecimal object:
BigDecimal a = new BigDecimal("3.048");
BigDecimal b = new BigDecimal(1000);
BigDecimal c = a.divide(b);
System.out.println(c); //0.003048
This is a consequence of the IEEE 754 floating point representation, not an error. To deal with it, round your result to an appropriate precision.
Use a BigDecimal for precise floating point calculations. Setting the scale allows you to specify precisely how far out you want to go for output.
import java.math.BigDecimal;
class Test{
public static void main(String[] args){
BigDecimal a = new BigDecimal("3.048");
BigDecimal b = new BigDecimal(1000);
BigDecimal c = a.divide(b).setScale(6);
System.out.println(c); //0.003048
}
}
Use BigDecimal for such precise allocations.
Btw the result for d is obviously right, because double has a machine encoding which cannot store the result, which you perceive as correct.

Categories