Everybody know, or at least, every programmers should know, that using the float
type could lead to precision errors. However, in some cases, an exact solution
The Decimal class is best for financial type addition, subtraction multiplication, division type problems:
>>> (1.1+2.2-3.3)*10000000000000000000
4440.892098500626 # relevant for government invoices...
>>> import decimal
>>> D=decimal.Decimal
>>> (D('1.1')+D('2.2')-D('3.3'))*10000000000000000000
Decimal('0.0')
The Fraction module works well with the rational number problem domain you describe:
>>> from fractions import Fraction
>>> f = Fraction(1) / Fraction(3)
>>> f
Fraction(1, 3)
>>> f * 3 < 1
False
>>> f * 3 == 1
True
For pure multi precision floating point for scientific work, consider mpmath.
If your problem can be held to the symbolic realm, consider sympy. Here is how you would handle the 1/3 issue:
>>> sympy.sympify('1/3')*3
1
>>> (sympy.sympify('1/3')*3) == 1
True
Sympy uses mpmath for arbitrary precision floating point, includes the ability to handle rational numbers and irrational numbers symbolically.
Consider the pure floating point representation of the irrational value of √2:
>>> math.sqrt(2)
1.4142135623730951
>>> math.sqrt(2)*math.sqrt(2)
2.0000000000000004
>>> math.sqrt(2)*math.sqrt(2)==2
False
Compare to sympy:
>>> sympy.sqrt(2)
sqrt(2) # treated symbolically
>>> sympy.sqrt(2)*sympy.sqrt(2)==2
True
You can also reduce values:
>>> import sympy
>>> sympy.sqrt(8)
2*sqrt(2) # √8 == √(4 x 2) == 2*√2...
However, you can see issues with Sympy similar to straight floating point if not careful:
>>> 1.1+2.2-3.3
4.440892098500626e-16
>>> sympy.sympify('1.1+2.2-3.3')
4.44089209850063e-16 # :-(
This is better done with Decimal:
>>> D('1.1')+D('2.2')-D('3.3')
Decimal('0.0')
Or using Fractions or Sympy and keeping values such as 1.1
as ratios:
>>> sympy.sympify('11/10+22/10-33/10')==0
True
>>> Fraction('1.1')+Fraction('2.2')-Fraction('3.3')==0
True
Or use Rational in sympy:
>>> frac=sympy.Rational
>>> frac('1.1')+frac('2.2')-frac('3.3')==0
True
>>> frac('1/3')*3
1
You can play with sympy live.
If you are new to Decimal, this post is relevant: Python floating point arbitrary precision available?
The essential idea from the answers and comments is that for computationally tough problems where precision is needed, you should use the mpmath
module https://code.google.com/p/mpmath/. An important observation is that,
The problem with using Decimal numbers is that you can't do much in the way of math functions on Decimal objects
is there a way to have a Decimal type with an infinite precision?
No; for any non-empty interval on the real line, you cannot represent all the numbers in the set with infinite precision using a finite number of bits. This is why Fraction
is useful, as it stores the numerator and denominator as integers, which can be represented precisely:
>>> Fraction("1.25")
Fraction(5, 4)
So, my question is: is there a way to have a Decimal type with an infinite precision?
No, since storing an irrational number would require infinite memory.
Where Decimal
is useful is representing things like monetary amounts, where the values need to be exact and the precision is known a priori.
From the question, it is not entirely clear that Decimal
is more appropriate for your use case than float
.