I recently ask a question about weird java double floor rounding, and got answers to use BigDecimals instead, so tried the following code:
BigDecimal velocity = new BigDecimal(-0.07);
BigDecimal afterMultiplyingBy200 = velocity.multiply( new BigDecimal(200.0) );
BigDecimal floored = afterMultiplyingBy200.setScale(0, RoundingMode.FLOOR);
System.out.println("After multiplication " + afterMultiplyingBy200);
System.out.println("floored value is " + floored);
And I'm getting following results
After multiplication -14.000000000000001332267629550187848508358001708984375000
floored value is -15
It seems that even using BigDecimal I can't get correct value for multiplying -0.07 by 200, is there anything that I can do to get exactly -14.0?
From my answer to that question:
When the code is compiled or interpreted, your “0.1” is already
rounded to the nearest number in that format, which results in a small
rounding error even before the calculation happens.
The problem is that new BigDecimal(-0.07); uses a double literal to initialize the BigDecimal - so the error still happens. Use the BigDecimal constructor that takes a String instead.
You should use the string constructor to avoid rounding errors due to the use of doubles:
BigDecimal velocity = new BigDecimal("-0.07");
BigDecimal afterMultiplyingBy200 = velocity.multiply(new BigDecimal("200"));
Javadoc extract for the constructor using doubles - emphasis mine:
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.
The problem is right here:
BigDecimal velocity = new BigDecimal(-0.07);
-0.07 cannot be represented exactly as a double, so the literal value passed to the BigDecimal constructor ends up being slightly different than -0.07. BigDecimal just takes that approximate value and runs with it, producing the results you're seeing.
Try:
BigDecimal velocity = new BigDecimal(-7, 2);
BigDecimal afterMultiplyingBy200 = velocity.multiply( new BigDecimal(2, -2) );
According to the documentation, ROUND_FLOOR rounds towards negative infinity, thus rounding -14.000... to -15.
You should have a look at RoundingMode, especially HALF_UP.
Related
This question already has answers here:
BigDecimal from Double incorrect value?
(4 answers)
Convert double to BigDecimal and set BigDecimal Precision
(8 answers)
Closed 4 years ago.
I want to retrieve the value of the bigDecimal with the exact value as it was instantiated here
BigDecimal balance = new BigDecimal(2300000870000000000067.7797);
The value I retrieve at the moment using balance is 2300000869999999975424.
Can you please advise how can I retrieve it as 2300000870000000000067.7797 itself?
You have used java.math.BigDecimal.BigDecimal(double val) constructor.
From JavaDoc:
java.math.BigDecimal.BigDecimal(double val)
Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value. The scale of the returned BigDecimal is the smallest value such that (10scale × val) is an integer.
Notes:
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.
Here First point suggests that :
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.
Second point suggests to use the constructor with string argument for exact value.
This is the reason for difference of value.
You are trying to use a literal number that cannot fit in a double which has a maximum of 15 decimals precision - probably why you want to use BigDecimal in the first place. So your number is converted to the most acurate representation in a double before initialising BigDecimal. Then the BigDecimal contructor compounds the error by messing up the already messed up double.
You will have to represent numbers as Strings to get that precision.
double x = 2300000870000000000067.7797d;
System.out.println("double:"+x);
BigDecimal balance = new BigDecimal(2300000870000000000067.7797);
System.out.println("balance:"+balance);
BigDecimal stringbased = new BigDecimal("2300000870000000000067.7797");
System.out.println("stringbased:"+stringbased);
Prints
double:2.30000087E21
balance:2300000869999999975424
stringbased:2300000870000000000067.7797
The java doc itself suggest about BigDecimal(Double val):
The results of this constructor can be somewhat unpredictable.
You should use the following instead:
BigDecimal balance = new BigDecimal("2300000870000000000067.7797");
I have two pieces of code new BigDecimal("1.240472701") and new BigDecimal(1.240472701). Now if i use compareTo method of java on both the methods then i get that they are not equal.
When i printed the values using System.out.println() method of java. I get different results for both the values. For example
new BigDecimal("1.240472701") -> 1.240472701
new BigDecimal(1.240472701) -> 1.2404727010000000664291519569815136492252349853515625
So i want to understand what could be reason for this?
You can refer the Java doc of public BigDecimal(double val) for this:
public BigDecimal(double val)
Translates a double into a BigDecimal
which is the exact decimal representation of the double's binary
floating-point value. The scale of the returned BigDecimal is the
smallest value such that (10^scale × val) is an integer.
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.
The string "1.240472701" is a textual representation of a decimal value. The BigDecimal code parses this and creates a BigDecimal with the exact value represented in the string.
But the double 1.240472701 is merely a (close) approximation of that exact decimal value. Double cannot represent all decimal values exactly, so the exact value stored in the double differs slightly. If you pass that to a BigDecimal, it takes that differing value and turns it into an exact BigDecimal. But the BigDecimal only has the inexact double to go by, it does not know the exact text representation. So it can only represent the value in the double, not the value of the source text.
In the first case:
String --> BigDecimal
Because BigDecimal is made to exactly represent decimal values, that conversion is exact.
In the second case:
1 2
Source code text --> double --> BigDecimal
In the second case, precision is lost in the first conversion (1). The second conversion (2) is exact, but the input -- the double -- is an inexact representation of the source code text 1.240472701 (in reality, it is 1.2404727010000000664291519569815136492252349853515625).
So: never initialize a BigDecimal with a double, if you can avoid it. Use a string instead.
That is why the first BigDecimal is exact and the second is not.
Since user thegauravmahawar provided the answer from docs. Yes, it is because of Scaling in BigDecimal case.
So the values might seem equal to You but internally java uses Scaling while storing the value of BigDecimal type.
Reason: Scaling.
Improvement:
You could call setScale to the same thing on the numbers you're comparing:
like this
new BigDecimal ("7.773").setScale(2).equals(new BigDecimal("7.774").setScale (2))
This will save you from making any mistake.
It is well documented that using a double can lead to inaccuracies and that BigDecimal guarantees accuracy so long as there are no doubles in the mix.
However, is accuracy guaranteed if the double in question is a small whole number?
For example, although the following will be inaccurate/unsafe:
BigDecimal bdDouble = new BigDecimal(0.1d); // 0.1000000000000000055511151231257827021181583404541015625
will the following always be accurate/safe?
BigDecimal bdDouble = new BigDecimal(1.0d); // 1
Is it safe to assume that small whole number doubles are safe to use with BigDecimals - if so, what is the smallest whole number that would introduce an inaccuracy?
>> Additional info in response to initial answers:
Thanks for the answers. Very helpful.
Just to add a little more detail, I have a legacy interface which supplies doubles, but I can be certain that these doubles will represent whole numbers having being themselves converted from Strings to doubles via Double.parseDouble(String) where the String is a guaranteed whole number representation.
I do not want to create a new interface which passes me Strings or BigDecimals if I can avoid it.
I can immediately convert the double to a BigDecimal on my side of the interface and make all internal calculations using BigDecimal calls, but I want to be sure that is as safe as creating a new BigDecimal/String interface.
Given that in my original example using 0.1d does not accurately result in 0.1, as shown by the fact that the actual BigDecimal is 0.1000000000000000055511151231257827021181583404541015625, it appears that some fractions will introduce an inaccuracy.
On the other hand, given that in my original example using 1.0d does accurately results in 1, it appears that whole numbers retain accuarcy. It appears that this is guaranteed up to a value of 2^53, if I understand your answers correctly.
Is that a correct assumption?
The BigDecimal aspect isn't as relevant to this question as "what is the range of integers that can be exactly represented in double?" - in that every finite double value can be represented exactly by BigDecimal, and that's the value you'll get if you call the BigDecimal(double) constructor. So you can be confident that if the value you wish to represent is an integer which is exactly representable by a double, if you pass that double to the BigDecimal constructor, you'll get a BigDecimal which exactly represents the same integer.
The significand of a double is 52 bits. Due to normalization, that means you should expect to be able to store integer values in the range [-253, 253] exactly. Those are pretty large numbers.
Of course, if you're only in the business of representing integers, it's questionable as to why you're using double at all... and you need to make sure that any conversions you're using from original source data to double aren't losing any information loss - but purely on the matter of "what range of integers are exactly representable as double values" I believe the above is correct...
A short answer is no. Because of the way a floating point variable is stored in memory there is no "small" value 0.000001 uses the same number of bits as 100000, every value is represented in the same way 0.xxx..eyy
A better way to initialize a BigDecimal is to initialize it with a string.
BigDecimal bdDouble = new BigDecimal("0.1");
I am using the following code where k is a BigDecimal:
Apfloat power = ApfloatMath.pow(new Apfloat(-1), new Apfloat(k));
Apfloat seems to have methods like intValue(), doubleValue(), floatValue() but I can't see how I can convert Apfloat to BigDecimal.
BigDecimals are best generated from Strings or using the BigDecimal.valueOf( double) static constructor.
For your specific requirement, start with the following:
public static asBigDecimal (Apfloat val) {
return BigDecimal.valueOf( val.doubleValue());
}
Calling new BigDecimal(double) is specifically to be avoided, since it generates exact decimal representations of the floating-point bits & can somewhat unpredictably require vast numbers of decimal digits to do so.
On the other hand, BigDecimal.valueOf( double) and Double.toString( double) have specific logic to represent doubles -> decimal without spurious deep decimalization.
See:
java.math.BigDecimal.BigDecimal(double)
Quoting from the Javadoc:
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.
According to the JavaDoc for BigDecimal, the compareTo function does not account for the scale during comparison.
Now I have a test case that looks something like this:
BigDecimal result = callSomeService(foo);
assertTrue(result.compareTo(new BigDecimal(0.7)) == 0); //this does not work
assertTrue(result.equals(new BigDecimal(0.7).setScale(10, BigDecimal.ROUND_HALF_UP))); //this works
The value I'm expecting the function to return is 0.7 and has a scale of 10. Printing the value shows me the expected result. But the compareTo() function doesn't seem to be working the way I think it should.
What's going on here?
new BigDecimal(0.7) does not represent 0.7.
It represents 0.6999999999999999555910790149937383830547332763671875 (exactly).
The reason for this is that the double literal 0.7 doesn't represent 0.7 exactly.
If you need precise BigDecimal values, you must use the String constructor (actually all constructors that don't take double values will work).
Try new BigDecimal("0.7") instead.
The JavaDoc of the BigDecimal(double) constructor has some related notes:
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.
So to summarize: If you want to create a BigDecimal with a fixed decimal value, use the String constructor. If you already have a double value, then BigDecimal.valueOf(double) will provide a more intuitive behaviour than using new BigDecimal(double).