0

Why is it that in python, some calculations are so much more imprecise than others?

Ex.

x=0.1
y=x+x+x
print(y==0.3)
print(y)

returns

False
0.3000000000004

Whereas if I start with x as 0.25 or 0.5, print(y==0.75) and print(y==2.25) both return True.

Shrey Joshi
  • 1,066
  • 8
  • 25
  • Does [this](https://stackoverflow.com/a/588014/12555857) post answer your question? – rkochar Mar 10 '21 at 18:00
  • A float can only represent about 2^64 different values _exactly_. 0.1 and 0.3 is not one of them. `0.3000000000004...` is the result of 3*(0.1 actually its closes FP value) ). 0.25 and 0.75 are exactly representable. – chux - Reinstate Monica Mar 10 '21 at 18:12
  • Before looking at "some calculations ", first look at `x=0.1` and see what _exact_ value `x` has. It is not 0.1. – chux - Reinstate Monica Mar 10 '21 at 18:17
  • How do you figure a test for equality demonstrates “some calculations are so much more imprecise”? If two numbers differ by just one part in a googol, they are unequal, so `==` reports they are unequal even if they have been calculated with great precision and accuracy. – Eric Postpischil Mar 11 '21 at 00:54

1 Answers1

2

Python's floats are internally represented in base 2 over a limited number bits (e.g. 64). This means that some rational values cannot be stored exactly. In the same way that 2/3 needs an infinite number of decimal places in base 10 and ends up being rounded to 0.6666....6667 , there are fractions in base 2 (different ones) that need a similar type of rounding.

You can use the decimal module's Decimal class to manipulate numbers in a more familiar base-10 representation.

from decimal import Decimal

x = Decimal("0.1")
y = x+x+x
print(y == Decimal("0.3")) # True
print(y) # 0.3

Note that i'm initializing the numbers from a string because Decimal(0.3) would make an imprecise decimal value from the already approximated 0.3 float value.

In the end, this just moves the problem to different values as you can see here:

x = Decimal(1)/Decimal(3)
y = x+x+x
print(y) # 0.9999999999999999999999999999

To completely avoid this precision issue with rational numbers, you would have to use the fractions module. But then you won't be able to perform some operations and will still be limited for irrational values (and transcendentals such as π and e).

x = Fraction(1,3)  # 1/3
y = x+x+x
print(y)           # 1

x = Fraction(1,10) # 0.1
y = x+x+x
print(y)           # 3/10
print(float(y))    # 0.3
Alain T.
  • 40,517
  • 4
  • 31
  • 51