John
John

Reputation: 497

Tell sympy to assume float is exact

I want to convert a numpy float to sympy and tell sympy that it is exact (e.g., it has an infinite number of 0 trailing the decimals I specify). I have tried:

from sympy import Rational, Float
num = "52.70517376823048"
num_sympy = Rational(num)
num_sympy.evalf(100)
>>> 52.70517376823048

which is not what I am after, UNLESS sympy is hiding the trailing zeros. How can I test, and is there a better way to tell sympy that a number is exact?

Upvotes: 1

Views: 59

Answers (2)

Oscar Benjamin
Oscar Benjamin

Reputation: 14530

The code shown in the question prints lots of trailing zeros when I run it:

In [17]: from sympy import Rational, Float
    ...: num = "52.70517376823048"
    ...: num_sympy = Rational(num)
    ...: num_sympy.evalf(100)
Out[17]: 52.70517376823048000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Note that if you are doing something like this:

num_sympy.evalf(100)
print(num_sympy)

Then you should change it to:

num_sympy = num_sympy.evalf(100)
print(num_sympy)

Upvotes: 2

ti7
ti7

Reputation: 18866

If you have a fixed-point, rather than a floating-point value, you use the builtin Decimal or rewrite it as a SymPy Rational

Directly from the string (likely the best/easiest)

>>> Rational("52.70517376823048").evalf(100)
52.70517376823048000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Other Examples

>>> Decimal("52.70517376823048")
Decimal('52.70517376823048')
>>> Decimal("52.70517376823048000")
Decimal('52.70517376823048000')
>>> Rational(5270517376823048, 10**14)
658814672102881/12500000000000
>>> Rational(5270517376823048, 10**14).evalf(100)
52.70517376823048000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Do watch out when worrying about small changes in the low bits of floats for XY Problems, as ideally the final result(s) you want are inside the overall accuracy available and so they can be ignored/dropped

Additionally, be aware that if you have a value that's not already an IEEE 754 float, it needs to be represented carefully (probably as a string) to avoid intermediately transforming it into one

>>> Rational(52.70517376823048).evalf(50)
52.705173768230480391139280982315540313720703125000
>>> Rational("52.70517376823048").evalf(50)
52.705173768230480000000000000000000000000000000000

See also Is floating-point math broken?

Upvotes: 2

Related Questions