views:

304

answers:

7

This is in reference to the comments in this question:

This code in Java produces 12.100000000000001 and this is using 64-bit doubles which can present 12.1 exactly. – Pyrolistical

Is this true? I felt that since a floating point number is represented as a sum of powers of two, you cannot represent 12.1 exactly, no matter how many bits you have. However, when I implemented both the algorithms and printed the results of calling them with (12.1, 3) with many significant digits, I get, for his and mine respectively:

12.10000000000000000000000000000000000000000000000000000000000000000000000000 12.10000000000000100000000000000000000000000000000000000000000000000000000000

I printed this using String.format("%76f"). I know that's more zeros than necessary, but I don't see any rounding in the 12.1 .

+12  A: 

No. As others noted in followups to his comment, no sum of (a finite number of) powers of two can ever add up to exactly 12.1. Just like you can't represent 1/3 exactly in base ten, no matter how many digits you use after the decimal point.

Aric TenEyck
How come the numbers printed are so exact then? The 1st answer is definitely more precise than the 2nd.
Claudiu
heh also the 'others' following up were me =P.
Claudiu
@Claudio - from what I can make out, you are rounding a `double` approximation of 12.1 to a number that ... when converted back to decimal ... is very close to 12.1. That's not surprising.
Stephen C
@Claudio - in the case where the resulting decimal number appears to be exactly 12.1, this is definitely an artefact of the library routine you are using to convert a `double` to a decimal string. It is rounding to a "human friendly" version of the number rather than displaying the mathematically closest decimal number to the `double` you have provided.
Stephen C
+11  A: 

In binary, 12.1 is:

1100.000110011001100110011...

Since this doesn't terminate, it can't be represented exactly in the 53 significand bits of a double, or any other finite-width binary floating-point type.

Stephen Canon
+8  A: 

Try to express 0.1 in binary:
0.5 is too big
0.25 is too big
0.125 is too big
0.0625 fits, and leaves a remainder of 0.0375
0.03125 fits, and leaves a remainder of 0.00625
0.015625 is too big
0.0078125 is too big
0.00390625 fits, and leaves a remainder of 0.00234375
0.001953125 fits, and leaves a remainder of 0.000390625

It's going to keep repeating indefinitely, creating a base 2 value of 0.00011001100...

No, it can't be expressed exactly in a double. If Java supports BCD, or fixed point decimal, that would work exactly.

wallyk
Java does support decimals, see java.math.BigDecimal.
Keith Randall
+3  A: 

Not in binary, no. If you'll allow me to be fanciful, you could in "floating point binary coded decimal" (which, to the best of my knowledge, has never been implemented):

12.1 = 0000 . 0001 0010 0001 * (10^2)

In binary all non-zero values are of the form 1.xyz * m, and IEEE form takes advantage of this to omit the leading 1. I'm not sure what the equivalent is for FP-BCD, so I've gone for values of the form 0.xyz * m instead.

Edmund
Correct, and well done for thinking outside the box.
Pool
'"floating point binary coded decimal" (which, to the best of my knowledge, has never been implemented)' -- oops, you need to read your history. It was implemented around 50 years ago.
Windows programmer
P.S. That is, it was implemented in hardware around 50 years ago, though it was also implemented in software before and after that.
Windows programmer
That is interesting and in hindsight not surprising. What I said was still correct, though: "to the best of my knowledge" etc. ;-)
Edmund
Actually the IEEE 754 floating point standard defines decimal floating point formats in densely packed decimal (10 bits = 3 digits). I've not seen those in the wild, though. http://en.wikipedia.org/wiki/IEEE_754-2008#Basic_formats
starblue
@starblue -- Thanks for that info. It makes sense to pack things a bit more efficiently (2 bits, in fact). Whether you can honestly call it "decimal" when it's base 10^3 is another matter... ;-)
Edmund
The point is representing decimal fractions exactly, and it does that. I suppose it's for applications in finance. And "millimal" would sound strange ... :-)
starblue
A: 

One option that you can use is to not store v=0.1, but instead store v10=1. Just divide by 10 when needed ( the division will create truncation error in your result but v will still be OK )

In this case you're basically doing a fixed point hack, but keeping the number in a float. But its usually not worth doing this unless you really have to.

Michael Anderson
+2  A: 

I suggest reading What Every Computer Scientist Should Know About Floating Point Arithmetic. Then you'll know for sure. :)

Qberticus
everything java programmers should know about floats: they are not precise:) that's good enough for me.
irreputable
+2  A: 

A way to see what the double is fairly exactly is to convert it to BigDecimal.

// prints 12.0999999999999996447286321199499070644378662109375
System.out.println(new BigDecimal(12.1));
Peter Lawrey