473,375 Members | 1,542 Online

Floating point Arithmetic seems odd in Python

Expand|Select|Wrap|Line Numbers
1. >>> trans = [{'debit':'50.6'},{'debit':'20.2'}]
2. >>> for row in trans:
3.     row['debit'] = float(row['debit'])
4.
5. >>> trans
6. [{'debit': 50.600000000000001}, {'debit': 20.199999999999999}]
7.
I Just want to convert some string to float but I get these weird numbers. Is it a bug in Python or am I doing something wrong?
Oct 27 '07 #1
7 1408
bvdet
2,851 Expert Mod 2GB
Expand|Select|Wrap|Line Numbers
1. >>> trans = [{'debit':'50.6'},{'debit':'20.2'}]
2. >>> for row in trans:
3.     row['debit'] = float(row['debit'])
4.
5. >>> trans
6. [{'debit': 50.600000000000001}, {'debit': 20.199999999999999}]
7.
I Just want to convert some string to float but I get these weird numbers. Is it a bug in Python or am I doing something wrong?
You are doing nothing wrong, nor is there a bug in Python. Floating point numbers cannot always be represented with 100% accuracy. When testing for equality, it is necessary to compare the numbers to a very small one. Here is an example:
Expand|Select|Wrap|Line Numbers
1. ....def equiv(self, other, epsilon = 0.000001):
2.         '''
3.         Compares two vectors, returns a tuple of 1, 0, or -1 values
4.         Interactive example:
5.         >>> Point(1,1,5).equiv(Point(2,0,5))
6.         (-1, 1, 0)
7.         >>>
8.         '''
9.         def comp(x,y):
10.             if abs(x-y) < epsilon: return 0
11.             elif x > y: return 1
12.             else: return -1
13.         return tuple(map(comp, self, other))
Oct 27 '07 #2
bartonc
6,596 Expert 4TB
Expand|Select|Wrap|Line Numbers
1. >>> trans = [{'debit':'50.6'},{'debit':'20.2'}]
2. >>> for row in trans:
3.     row['debit'] = float(row['debit'])
4.
5. >>> trans
6. [{'debit': 50.600000000000001}, {'debit': 20.199999999999999}]
7.
I Just want to convert some string to float but I get these weird numbers. Is it a bug in Python or am I doing something wrong?
If you take it down to the "ones-and-zeros" where floating point numbers (and every thing else) must eventually be represented, you'll find that there is no way to store 20.2 precisely. Given the number of bits available, that's a pretty good approximation, though. Going the other way (float to text) is handled correctly:
Expand|Select|Wrap|Line Numbers
1. >>> a = 20.2
2. >>> a
3. 20.199999999999999
4. >>> "%.2f" %a
5. '20.20'
6. >>>
Oct 27 '07 #3
Thanks a lot bartonc and bvdet!

I was afraid I would have to rip up my whole code to work around this ;-). Interesting with that binary-thing. Never thought about that before.

Hahaha, just found the python-docs:
[HTML]Note that this is in the very nature of binary floating-point: this is not a bug in Python, and it is not a bug in your code either.
[/HTML]

Guess I should have looked there first, if I knew what I was looking for :-).
Oct 27 '07 #4
bartonc
6,596 Expert 4TB
Thanks a lot bartonc and bvdet!

I was afraid I would have to rip up my whole code to work around this ;-). Interesting with that binary-thing. Never thought about that before.

Hahaha, just found the python-docs:
[HTML]Note that this is in the very nature of binary floating-point: this is not a bug in Python, and it is not a bug in your code either.
[/HTML]

Guess I should have looked there first, if I knew what I was looking for :-).
Just thought that I'd add the link: B. Floating Point Arithmetic: Issues and Limitations

Thanks for supplying that quote from the docs.
Oct 27 '07 #5
elcron
43
A wrapper class could fix your representation problem. Though you would have to create instances of it rather than float.
Expand|Select|Wrap|Line Numbers
1. class Decimal(float):
2.     def __repr__(self):
3.         return '%.2f' %(self)
4.
Oct 27 '07 #6
bartonc
6,596 Expert 4TB
A wrapper class could fix your representation problem. Though you would have to create instances of it rather than float.
Expand|Select|Wrap|Line Numbers
1. class Decimal(float):
2.     def __repr__(self):
3.         return '%.2f' %(self)
4.
Nice thought, but there is a name clash there...
Expand|Select|Wrap|Line Numbers
1. >>> from decimal import Decimal
Oct 27 '07 #7
bartonc
6,596 Expert 4TB
Nice thought, but there is a name clash there...
Expand|Select|Wrap|Line Numbers
1. >>> from decimal import Decimal
And here's that link:5.6 decimal -- Decimal floating point arithmetic[HTML]New in version 2.4.

The decimal module provides support for decimal floating point arithmetic. It offers several advantages over the float() datatype:

* Decimal numbers can be represented exactly. In contrast, numbers like 1.1 do not have an exact representation in binary floating point. End users typically would not expect 1.1 to display as 1.1000000000000001 as it does with binary floating point.[/HTML]
Oct 27 '07 #8