Reputation: 89693
The source for round in apache commons looks like this:
public static double round(double x, int scale, int roundingMethod) {
try {
return (new java.math.BigDecimal(Double.toString(x)).setScale(scale, roundingMethod)).doubleValue();
} catch (NumberFormatException ex) {
if (Double.isInfinite(x)) {
return x;
} else {
return Double.NaN;
}
}
}
I was wondering, when creating the BigDecimal
why did they chose to convert the double to a string (using the Double.toString
) instead of simply using the double itself?
In other words, what's wrong with this? :
public static double round(double x, int scale, int roundingMethod) {
try {
return (new java.math.BigDecimal(x).setScale(scale, roundingMethod)).doubleValue();
} catch (NumberFormatException ex) {
if (Double.isInfinite(x)) {
return x;
} else {
return Double.NaN;
}
}
}
Upvotes: 5
Views: 1394
Reputation: 20836
However, I found one question:
System.out.println(java.math.BigDecimal.valueOf(0.1000000000000000055511151231257827021181583404541015625).toString());
It will print 0.1.
I think the key is java.lang.Double.toString(double d). When we input 0.1(In Java, the exact value is 0.1000000000000000055511151231257827021181583404541015625), the function will drop the tail and we get 0.1. However. if we input 0.1000000000000000055511151231257827021181583404541015625, it always drop the tail and we also get 0.1 which is against our expectation.
So we must be aware of our requirement to choose java.math.BigDecimal.valueOf(double val) or java.math.BigDecimal.BigDecimal(double val).
Upvotes: 1
Reputation: 14549
It's because the result of BigDecimal(double)
constructor is unpredictable as mentioned in javadoc.
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
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.
Test Case:
System.out.println(java.math.BigDecimal.valueOf(0.1).toString());
System.out.println(new java.math.BigDecimal(0.1).toString());
Upvotes: 7
Reputation: 2674
It seems to me that the key is in this statement:
BigDecimal(double val)
Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value.
versus
BigDecimal(String val)
Translates the string representation of a BigDecimal into a BigDecimal.
By going to String first, you just see the double as a BigDecimal, but if construct directly from a double, you're working with the bit-level representation, and I'm willing to bet you'd get a subtly different answer. Remember that floating-point values are represented using a sections of the 64-bits for the whole part, and a section for the fraction part, and a section to represent an exponent (in base 2) -- it's all very esoteric stuff, differs from machine to machine, and I think the only formal definition is found in the accursed Necronomicon.
Update: What Borodin said.
Upvotes: 1
Reputation: 126742
From the documentation here for the constructor BigDecimal(String val)
:
Note: For values other than float and double NaN and ±Infinity, this constructor is compatible with the values returned by Float.toString(float) and Double.toString(double). 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.
Upvotes: 4
Reputation: 425178
You don't have to convert to String.
Use the static method BigDecimal.valueOf(double val)
:
To use in your code:
...
return (BigDecimal.valueOf(x).setScale(scale, roundingMethod)).doubleValue();
....
Upvotes: 1