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?

+3


source to share


4 answers


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.

+7


source


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

.

+5


source


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

+4


source


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 "")

      

+2


source







All Articles