I’ve typed this into python shell:
>>> 0.1*0.1
0.010000000000000002
I expected that 0.1*0.1 is not 0.01, because I know that 0.1 in base 10 is periodic in base 2.
>>> len(str(0.1*0.1))
4
I expected to get 20 as I’ve seen 20 characters above. Why do I get 4?
>>> str(0.1*0.1)
'0.01'
Ok, this explains why I len gives me 4, but why does str return '0.01'?
>>> repr(0.1*0.1)
'0.010000000000000002'
Why does str round but repr not? (I have read this answer, but I would like to know how they have decided when str rounds a float and when it doesn’t)
>>> str(0.01) == str(0.0100000000001)
False
>>> str(0.01) == str(0.01000000000001)
True
So it seems to be a problem with the accuracy of floats. I thought Python would use IEEE 754 single precicion floats. So I’ve checked it like this:
#include <stdint.h>
#include <stdio.h> // printf
union myUnion {
uint32_t i; // unsigned integer 32-bit type (on every machine)
float f; // a type you want to play with
};
int main() {
union myUnion testVar;
testVar.f = 0.01000000000001f;
printf("%f\n", testVar.f);
testVar.f = 0.01000000000000002f;
printf("%f\n", testVar.f);
testVar.f = 0.01f*0.01f;
printf("%f\n", testVar.f);
}
I got:
0.010000
0.010000
0.000100
Python gives me:
>>> 0.01000000000001
0.010000000000009999
>>> 0.01000000000000002
0.010000000000000019
>>> 0.01*0.01
0.0001
Why does Python give me these results?
(I use Python 2.6.5. If you know of differences in the Python versions, I would also be interested in them.)
The crucial requirement on
repris that it should round-trip; that is,eval(repr(f)) == fshould giveTruein all cases.In Python 2.x (before 2.7)
reprworks by doing aprintfwith format%.17gand discarding trailing zeroes. This is guaranteed correct (for 64-bit floats) by IEEE-754. Since 2.7 and 3.1, Python uses a more intelligent algorithm that can find shorter representations in some cases where%.17ggives unnecessary non-zero terminal digits or terminal nines. See What’s new in 3.1? and issue 1580.Even under Python 2.7,
repr(0.1 * 0.1)gives"0.010000000000000002". This is because0.1 * 0.1 == 0.01isFalseunder IEEE-754 parsing and arithmetic; that is, the nearest 64-bit floating-point value to0.1, when multiplied by itself, yields a 64-bit floating-point value that is not the nearest 64-bit floating-point value to0.01:The difference between
reprandstr(pre-2.7/3.1) is thatstrformats with 12 decimal places as opposed to 17, which is non-round-trippable but produces more readable results in many cases.