Reputation: 86403
While working with DecimalFormat
, I was puzzled by its behavior when rounding numbers with fixed-point patterns. To make things more specific:
double n = 2082809.080589735D;
// Output the exact value of n
System.out.println("value: " + new BigDecimal(n).toPlainString());
System.out.println("double: " + n);
DecimalFormat format = new DecimalFormat("0.00000000");
System.out.println("format: " + format.format(n));
System.out.println("format (BD): " + format.format(new BigDecimal(n)));
The output of this snippet is:
value: 2082809.080589734949171543121337890625
double: 2082809.080589735
format: 2082809.08058974
format (BD): 2082809.08058973
From the first output line we notice that the actual value is below the half-way point between 2082809.08058973
and 2082809.08058974
(...49...
). Despite this, DecimalFormat
rounds the value upwards when provided with a double
argument.
Other values are rounded downwards:
value: 261285.2738465850125066936016082763671875
double: 261285.273846585
format: 261285.27384658
format (BD): 261285.27384659
This does not happen in all cases:
value: 0.080589734949171543121337890625
double: 0.08058973494917154
format: 0.08058973
format (BD): 0.08058973
value: 0.2738465850125066936016082763671875
double: 0.2738465850125067
format: 0.27384659
format (BD): 0.27384659
It seems to me that the formatted string for a double
is rounded using half-even rounding based on an imprecise decimal value produced with something along the lines of Double.toString()
, rather than the actual mathematical value represented by the double
in question. When the formatted precision is very close to (or more than) the precision provided by the double
type, things start becoming somewhat random.
In all the cases presented above, formatting the corresponding BigDecimal
seems to perform the rounding as expected.
I was unable to find any specification describing the proper behavior of DecimalFormat
in this case.
Is this behavior documented somewhere?
From a correctness point of view, wouldn't rounding the actual mathematical value be preferable?
I understand that people writing 1.0...35
would (naively?) expect it to be rounded to 1.0...4
, but 1.0...35
may not even be representable in any primitive data type available in Java...
EDIT:
I have submitted a report to Oracle - hopefully they will be able to either fix or clarify this issue.
Upvotes: 1
Views: 2311
Reputation: 86403
This was apparently a known issue that has been fixed in Java 8.
DecimalFormat
in Java 8 is supposed to always perform the rounding correctly, regardless of the format chosen by the developer. Naturally, this results in slightly incompatible behavior when compared to Java 7 - whether this is important or not would depend on the specifics of each application.
Upvotes: 1
Reputation: 3512
It looks like this could be due to double rounding.
The output marked "double" looks like it's correctly rounded to 16 digits. The output marked "format" looks like it's rounded from the 16 digit value (rounded half to even).
(I'd need to see more examples to verify this.)
Upvotes: 0
Reputation: 28767
According to the Java Language Specification, java uses IEEE 754 - 1985, while you (from the comments asumed IEEE754 - 2008)
The floating-point types are float and double, which are conceptually associated with the single-precision 32-bit and double-precision 64-bit format IEEE 754 values and operations as specified in IEEE Standard for Binary Floating-Point Arithmetic, ANSI/IEEE Standard 754-1985 (IEEE, New York).
Use setRoundingMode()
of DecimalFormat
to specify the rounding behavior.
Is this behavior documented somewhere?
See javadoc of DecimalFormat and the detailed in RoundingMode And in IEEE 754 - 1985 and in JLS (first statemen in this answer)
Default, as documented is the Half even rounding mode. HALF_EVEN
public static final RoundingMode HALF_EVEN
Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor. Behaves as for RoundingMode.HALF_UP if the digit to the left of the discarded fraction is odd; behaves as for RoundingMode.HALF_DOWN if it's even. Note that this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations. It is sometimes known as "Banker's rounding," and is chiefly used in the USA. This rounding mode is analogous to the rounding policy used for float and double arithmetic in Java.
Upvotes: 0