
| Key: |
DERBY-123
|
| Type: |
Bug
|
| Status: |
Closed
|
| Resolution: |
Fixed
|
| Priority: |
Major
|
| Assignee: |
Unassigned
|
| Reporter: |
Geoff Soutter
|
| Votes: |
1
|
| Watchers: |
1
|
|
If you were logged in you would be able to see more operations.
|
|
|
|
Environment:
|
Linux JDK 1.4.2
|
|
| Resolution Date: |
10/Feb/05 04:38 AM
|
|
When inserting a double value into a field defined as NUMERIC(a,b) using PreparedStatement.setDouble(), Derby may incorrectly round the number down.
For example, a NUMERIC(5,2) field with a double = 4.64 ends up with a value of 4.63 in the database. This works fine in Oracle and other databases.
The problem occurs because double cannot represent 4.64 exactly, so the actual value is 4.6399999... SQLDecimal.setCoreValue uses BigDecimal(double) which constructs a BigDecimal of 4.6399999... and then SQLDecimal.setWidth uses value.setScale(desiredScale, BigDecimal.ROUND_DOWN); Note that BigDecimal javadoc recommends that the double constructor be avoided for this reason.
One fix appears to be to change SQLDecimal.setCoreValue(double) to avoid using the double constructor of BigDecimal. Using Double.toString() and BigDecimal(String) looks as if it would work as expected, because Double.toString() has "special rounding abilities" and converts 4.639999... back to 4.64.
|
|
Description
|
When inserting a double value into a field defined as NUMERIC(a,b) using PreparedStatement.setDouble(), Derby may incorrectly round the number down.
For example, a NUMERIC(5,2) field with a double = 4.64 ends up with a value of 4.63 in the database. This works fine in Oracle and other databases.
The problem occurs because double cannot represent 4.64 exactly, so the actual value is 4.6399999... SQLDecimal.setCoreValue uses BigDecimal(double) which constructs a BigDecimal of 4.6399999... and then SQLDecimal.setWidth uses value.setScale(desiredScale, BigDecimal.ROUND_DOWN); Note that BigDecimal javadoc recommends that the double constructor be avoided for this reason.
One fix appears to be to change SQLDecimal.setCoreValue(double) to avoid using the double constructor of BigDecimal. Using Double.toString() and BigDecimal(String) looks as if it would work as expected, because Double.toString() has "special rounding abilities" and converts 4.639999... back to 4.64. |
Show » |
|
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 (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.
Parameters:
val - double value to be converted to BigDecimal.
Throws:
NumberFormatException - if val is infinite or NaN.
From the *Notes* section it is clear that double values cannto be stored precisely as the value that is represented since it might not be possible to represent it as a fraction of finite length, hence the behaviour here.
So it suggested we construct the BigDecimal value by using Double.toString() method and passing the value to BigDecimal constructor to build the exact value.