RandomQuestion
RandomQuestion

Reputation: 6998

Using DecimalFormat to format big double numbers

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

Answers (2)

user207421
user207421

Reputation: 311054

Your literal has 19 decimal digits but a double only has 15.9 decimal digits of precision.

Upvotes: 0

rgettman
rgettman

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

Related Questions