Reputation: 6998
I am trying to use DecimalFormat
to format double numbers to just print the number with 2 decimal places if it has fractional part else i want to print number as it is. When number is small, below code works fine but with big numbers, It's not working as expected.
DecimalFormat df = new DecimalFormat("#.##"); //have tried just # too
double d1 = 56789d;
System.out.println(df.format(d1)); //works fine - prints 56789
double d2 = 1234567879123456789d;
System.out.println(df.format(d2)); // does not work
Second outputs 1234567879123456770
while I want 1234567879123456789
. For double values with decimal part, I just want to retain two decimal points.
Any suggestions on what's going wrong?
Upvotes: 5
Views: 2988
Reputation: 311054
Your literal has 19 decimal digits but a double only has 15.9 decimal digits of precision.
Upvotes: 0
Reputation: 178343
The explanation as to why the parsed value is off by 19
lies in the how double
values are represented, as IEEE floating-point numbers with 53 bits of precision. That is, for large values as you're inputting, the precision is actually greater than 1
. The method Math.ulp
, for "unit in last place", gives the closest double
values can be apart at the magnitude of its argument.
double d2 = 1234567879123456789d;
DecimalFormat df = new DecimalFormat("#.##");
System.out.println(Math.ulp(d2));
System.out.println(df.format(d2));
This outputs
256.0
1234567879123456770
So, you get the closest double
value to the number you type in the source code. Section 3.10.2 of the JLS covers double
literals:
The elements of the types float and double are those values that can be represented using the IEEE 754 32-bit single-precision and 64-bit double-precision binary floating-point formats, respectively.
The details of proper input conversion from a Unicode string representation of a floating-point number to the internal IEEE 754 binary floating-point representation are described for the methods valueOf of class Float and class Double of the package java.lang.
And referring to Double.valueOf
javadocs:
s is regarded as representing an exact decimal value in the usual "computerized scientific notation" or as an exact hexadecimal value; this exact numerical value is then conceptually converted to an "infinitely precise" binary value that is then rounded to type double by the usual round-to-nearest rule of IEEE 754 floating-point arithmetic [...]
Additionally, this value is still within the range of long
values, which will still represent the integer value correctly.
long l2 = 1234567879123456789L;
DecimalFormat df = new DecimalFormat("#.##");
System.out.println(df.format(l2));
This outputs
1234567879123456789
Upvotes: 8