I believe that 17 decimal places should be enough to correctly represent an 8-byte float, such that it is round-trip safe (converted to a string and back without any loss).
But in this test, the number can go as high as 23, and probably higher if you increase the number of iterations.
Is this a flawed test and why?
And how do you ensure a round-trip integrity of a float in Python?
def TestLoop():
sFormat = ''
success = True
ff = [1.0/i for i in range(1,10000000)]
for n in range(17, 31):
sFormat = '{{:.{:d}f}}'.format(n)
success = True
for f in ff:
if f != float(sFormat.format(f)):
success = False
break
if success:
return(n)
return(-1)
n = TestLoop()
print('Lossless with ', n, ' decimal places.')
In my original test, I was operating on small numbers, so there were a lot of leading zeros, which are not significant digits. Floats require 17 significant digits to be represented correctly. By changing one line like so, I made the numbers larger and was able to succeed with only 16 digits after the decimal point.
ff = [10000000.0/i for i in range(1,10000000)]
The best approach seems to be to not use the format()
at all, but use repr()
or str()
instead.
This code here succeeds:
def TestLoop():
for i in range(1, 10000000):
f = 1.0 / i
if f != float(repr(f)):
print('Failed.')
return
print('Succeeded.')
return
TestLoop()
Another way that worked was to use 17 digits after the decimal point, but use the g
formatter instead of f
. This uses an exponent, so the leading zeros are eliminated.
if f != float('{:.17g}'.format(f)):