2

When I run this code,

print('{:.15f}'.format(123456789.55555555555555555555555555555555555555))
print('{:.25f}'.format(0.5555555555555555555555555555555555555555555555))

I get this output:

123456789.555555552244186
0.5555555555555555802271783

Where do those extra digits come from(123456789.555555552244186, 0.5555555555555555802271783) and why are they not zero?

I am guessing they are generated by the conversion algorithm and are meaningless, but I would like to have more info on this. It would be nice if the conversion algorithm made them zero.

mcu
  • 3,302
  • 8
  • 38
  • 64
  • 1
    Take a look at [Floating point](https://en.wikipedia.org/wiki/Floating_point). Standard Python (aka CPython) uses [IEEE 754 double precision](https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64) for its `float` type. – PM 2Ring Sep 27 '15 at 13:03
  • So, I understand how a floating point number operates in general. But why does the conversion algorithm keeps on converting, when it ran out of bits to convert? A float has a limited precision, unlike a decimal. – mcu Sep 27 '15 at 13:06
  • 1
    Ah, ok. That's a better question. :) I'm not sure why the conversion routine keeps on going, converting garbage bits. It looks like it continues for around 53 digits, which is curious, given that there are 53 significant _bits_ in an IEEE 754 double. Try changing the format spec to `{:.63f}` on your second number & you'll eventually see a bunch of zeroes. – PM 2Ring Sep 27 '15 at 13:16
  • Just because you can type a float literal `0.555...` doesn't mean Python is storing *that* value exactly. It's converting it to the closest fraction with a denominator that is a power of 2, and the decimal expansion of *that* fraction is what you are seeing. – chepner Sep 27 '15 at 13:16
  • @chepner: Certainly. But that still doesn't explain those wacky digits above & beyond the call of duty. :) Eg, 0.2=1/5=3/15, so in hex that's 0.33333... It's understandable that it'll start producing rubbish after the 16th place, but why does the conversion routine bother spitting out rubbish for another 30-odd digits? – PM 2Ring Sep 27 '15 at 13:24
  • Given that you've requested it, Python has to produce *something* or raise an error. The relative error should be insignificant, so that's easier to deal with than handling an error. It's undefined behavior as far as I can tell. – chepner Sep 27 '15 at 13:33
  • If this is only *something*, and not meaningful, I would prefer zeros instead. – mcu Sep 27 '15 at 13:44

1 Answers1

3

When you try to store these values what is actually stored is the closest representable double precision binary floating point value. And that is what is being printed. So these extra digits are actually meaningful - they represent the actual value that is stored.

See Rob Kennedy's useful web page which shows the closest representable floating point number to a given value. As you can see, the closest double precision value to your first value is:

+ 1234 56789.55555 55522 44186 40136 71875

And the closest double precision value to your second value is:

+ 0.55555 55555 55555 58022 71783 25003 47867 60807 03735 35156 25

These values match what Python produces.

So, this is purely an issue of representability. More references on floating point:

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • OK, that makes sense. I guess. :) – PM 2Ring Sep 27 '15 at 13:33
  • So, one thing is for sure, Python and the [on line converter](http://pages.cs.wisc.edu/~rkennedy/exact-float) use the same conversion algorithm. I am still fuzzy on how they come up with these extra digits, math-wise. And why the algorithm gives up after about 53 digits, as *PM 2Ring* has shown. – mcu Sep 27 '15 at 13:48
  • It's the closest representable value. It's not a coincidence that Rob's page and Python do the same thing. This is governed by a standard IEEE754. Did you read the links I gave you so that you understand what that means. A double precision value has 53 binary bits of precision. – David Heffernan Sep 27 '15 at 13:51
  • Yes, but 53 binary bits do not relate to 53 decimal digits. Or do they? – mcu Sep 27 '15 at 13:52
  • My answer is accurate. Did you read the links yet? Do you understand what numbers are representable? Can you find representable numbers that are closer? 53 decimal digits is a red herring. The first number has 34 decimal digits. – David Heffernan Sep 27 '15 at 13:55
  • Numbers with more than 53 decimal digits *should* be closer, but I guess this is their arbitrary cutoff point. – mcu Sep 27 '15 at 13:57
  • No. It not arbitrary. Double precision binary floating point is not arbitrary. It has 53 binary digits of precision. And that's the cut off. If you want to understand this you'll need to really be clear on what is meant by representable, and how numbers are represented. – David Heffernan Sep 27 '15 at 13:58
  • `print('{:.100f}'.format(0.1))` produces `0.1000000000000000055511151231257827021181583404541015625000000000000000000000000000000000000000000000` They stopped with 55 *decimal* digits after the decimal point. I bet `0.1` could be represented more precisely if they used more *decimal* digits. I am still trying to digest that last link that you provided. – mcu Sep 27 '15 at 14:06
  • It's not represented by decimal digits. It's represented by binary digits. 53 of them. Did you read the links I gave you yet? You still seem to think that 64 bits can hold an arbitrary amount of information. I'm just repeating myself now. Try to understand how these values are represented. – David Heffernan Sep 27 '15 at 14:08
  • Ok, so the string representation would be more precise if they kept on converting instead of using trailing zeros. But they stopped at 55. – mcu Sep 27 '15 at 14:12
  • No. That's wrong. That value is exact. That's the closest double precision value to 0.1. – David Heffernan Sep 27 '15 at 14:19
  • But why the claim that a 64-bit float can be represented exactly with 17 decimal digits? – mcu Sep 27 '15 at 14:25
  • Because a decimal digit has more information than a binary digit. – David Heffernan Sep 27 '15 at 14:30
  • Yes, but it took them 55 decimal digits to represent `0.1`. I guess 17 decimal digits is enough for when you convert from a string to a float, but not the other way around? – mcu Sep 27 '15 at 14:35
  • 1
    55 decimal digits are for the **closest** double precision value to 0.1. I don't think you are really getting this. I recommend that you read the links I gave you. Concentrate on how a number is represented. – David Heffernan Sep 27 '15 at 14:37
  • I think I got it for the most part. `s='{:.100f}'.format(1/3); print(float(s)==float(s[:19]))` When converting from a string to a float, 17 significant decimal digits is enough, everything else is lost. But to represent a float in a string precisely, it may well take more than 17 decimal digits. – mcu Sep 27 '15 at 14:54
  • @coding4fun: Note that `2^-53` is a number with a lot of digits in decimal, although it is only one bit in the floating point value. Heck, `2^-10` is already `0.0009765625`, and that is also only one bit in a float. So you see exact values, and these have that many digits. This due to the fact that you are converting binary to decimal. If the exponent is not 0, then it gets even worse. – Rudy Velthuis Sep 28 '15 at 00:19
  • @RudyVelthuis You are not kidding. 2e-53 = 0.000000000000000000000000000000000000000000000000000020000000000000000615975242951574530368453090977309217149973291912142953202919462494985454702596019212913114791075752904401982724331737935330455258053916622884571552276611328125 That is __228__ places after the decimal point. – mcu Sep 28 '15 at 00:33
  • 2e-53 is not the same as 2^-53. I assume you meant the latter. – Rudy Velthuis Sep 28 '15 at 06:34
  • 2^-53 = 0.00000000000000011102230246251565404236316680908203125 Slightly less impressive, but still a lot of digits. But I see your point that a single binary bit results in many decimal digits when we try to represent a binary number in decimal notation. – mcu Sep 28 '15 at 08:18
  • 2^-500 = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030549363634996046820519793932136176997894027405723266638936139092812916265247204577018572351080152282568751526935904671553178534278042839697351331142009178896307244205337728522220355888195318837008165086679301794879136633899370525163649789227021200352450820912190874482021196014946372110934030798550767828365183620409339937395998276770114898681640625 A single set binary bit in the mantissa, but quite a number in the decimal notation. – mcu Sep 28 '15 at 08:43
  • Seems like you've got the hang of this now! – David Heffernan Sep 28 '15 at 08:45