A "round" number multiplied by 0.01 results in x.y00000000000001, not xy?
The reason I am asking this is because there is validation in OpenERP that drives me crazy:
>>> round(1.2 / 0.01) * 0.01
1.2
>>> round(12.2 / 0.01) * 0.01
12.200000000000001
>>> round(122.2 / 0.01) * 0.01
122.2
>>> round(1222.2 / 0.01) * 0.01
1222.2
As you can see, the second one round
returns an odd value.
Can someone explain to me why this is happening?
source to share
It really has nothing to round
do with , you can see the same problem if you just do 1220 * 0.01
:
>>> 1220*0.01
12.200000000000001
What you see here is the standard floating point problem.
You might want to read what Wikipedia has to say about floating point issues :
The fact that floating point numbers cannot accurately represent all real numbers, and that floating point operations cannot accurately represent true arithmetic operations, leads to many unexpected situations. This is due to the finite precision with which computers usually represent numbers.
Also see:
A simple example of floating point numerical instability: numbers are finite. let's say we store 4 digits after the dot in a given computer or language. 0.0001 multiplied by 0.0001 will result in something below 0.0001 and therefore it is not possible to store this result! In this case, if you calculate (0.0001 x 0.0001) / 0.0001 = 0.0001, this simple computer will not be accurate because it tries to multiply first and only then to divide. In javascript, fractions lead to similar inaccuracies.
source to share
The type float
you are using stores binary floating point numbers. Not every decimal number is exactly represented as float
. In particular, there is no exact representation of 1.2 or 0.01, so the actual number stored on the computer will differ slightly from the value recorded in the original code. This presentation error can cause calculations to give slightly different results from the exact mathematical result.
It's important to be aware of the possibility of small errors whenever you use floating point arithmetic, and write your code so that it works well even when the calculated values are not entirely correct. For example, you should consider rounding values to a certain number of decimal places when displaying them to the user.
You can also use a type decimal
that stores floating point decimal numbers. If you are using decimal
then 1.2 can be saved for sure. However, working with decimal
will degrade the performance of your code. You should only use it if the exact representation of decimal numbers is important. You also need to know, which decimal
does not mean that you will never have any problems. For example, 0.33333 ... has no precise idea of how decimal
.
source to share
The loss of precision from division is due to the fact that floating point numbers are stored, so you can see that this identity does not contain
>>> 12.2 / 0.01 * 0.01 == 12.2
False
bArmageddon, has provided a bunch of references that you should read, but I believe the output post does not suggest float will give accurate results unless you fully understand the limits of the view.
Especially don't use floats to represent the amount of money! which is a fairly common mistake
Python also has a decimal module which you might find useful
source to share
Others answered your question and mentioned that many numbers do not have an exact binary fractional representation. If you are used to working with decimal numbers only, it may seem odd that a good "round" number like 0.01 could be an infinite number on some other basis. In the spirit of "seeing is believing," here's a little Python program that will print the binary representation of any number to any number of digits.
from decimal import Decimal
n = Decimal("0.01") # the number to print the binary equivalent of
m = 1000 # maximum number of digits to print
p = -1
r = []
w = int(n)
n = abs(n) - abs(w)
while n and -p < m:
s = Decimal(2) ** p
if n >= s:
r.append("1")
n -= s
else:
r.append("0")
p -= 1
print "%s.%s%s" % ("-" if w < 0 else "", bin(abs(w))[2:],
"".join(r), "..." if n else "")
source to share