Exactness is the reason to use a CAS; one stray float silently turns exact results into approximations.
SymPy has its own number types: Integer, Rational and arbitrary-precision Float, plus exact constants pi, E, I (the imaginary unit) and oo (infinity). Exact numbers stay exact: Rational(1, 3) + Rational(1, 6) is 1/2, and sqrt(8) is automatically rewritten as 2*sqrt(2).
The classic trap is Python division. In x + 1/2, Python evaluates 1/2 to the float 0.5 before SymPy ever sees it, so the expression contains a Float. Fixes:
Rational(1, 2), orS(1)/2:S(1)is SymPy'sInteger(1), and dividing a SymPy integer by a Python int gives aRational.x/2is fine because the symbol is on the left of the operator.S("1/3")orsympify("1/3")parse a string exactly.
Floats are contagious: once a Float is in an expression, results involving it are approximate, and simplification has less to work with. Float itself carries a precision, so Float("0.1", 30) is more precise than the binary float 0.1.
Exact constants are symbolic objects: pi is not 3.14159..., it is the number pi. sin(pi) evaluates to 0 exactly, whereas math.sin(math.pi) gives 1.2246e-16. Convert to a decimal only at the end, with evalf() or N().
Example
from sympy import Rational, S, sqrt, pi, sin, symbols
import math
x = symbols("x")
print(x + 1/2) # x + 0.5 (a Python float sneaked in)
print(x + Rational(1, 2)) # x + 1/2
print(x + S(1)/2, x/2) # x + 1/2 x/2
print(Rational(1, 3) + Rational(1, 6)) # 1/2
print(sqrt(8), sqrt(2)**2) # 2*sqrt(2) 2
print(sin(pi), math.sin(math.pi)) # 0 1.2246467991473532e-16
print(pi.evalf(30)) # 3.14159265358979323846264338328Remember
- Rational(1, 2) or S(1)/2, never 1/2, for exact fractions
- sqrt, pi, E, I and oo are exact symbolic objects
- Convert to decimals only at the end
Watch out for
- Writing Rational(0.1) and getting 3602879701896397/36028797018963968: pass a string or two ints instead.