Python Floating Point Problem 3

Asked

Viewed 1,588 times

1

I’m writing an algorithm where variable increment float in 0.2, however, after a few increments instead of incrementing 2.2 for 2.4, for example, the program increments to 2.4000000000000004

I’ve read about this Python "bug" (I know it’s not a bug, but I couldn’t find a better word), but I can’t find the reference to study a solution. Follows the algorithm:

J1, J2, J3, I = 1.0, 2.0, 3.0, 0

while I <= 2:
    print('I={} J={}'.format(I, J1))
    print('I={} J={}'.format(I, J2))
    print('I={} J={}'.format(I, J3))
    J1 += 0.2
    J2 += 0.2
    J3 += 0.2
    I += 0.2

And the exit:

inserir a descrição da imagem aqui

  • 2

    If it’s just a display issue, you can format the output with {:.2}, limiting thus only two decimal places. If used for mathematical operations, use the module decimal as already answered.

  • Related: https://answall.com/q/219211/64969

  • In this answer I show an example calculation with a class of numbers that shares the same problem of floating point numbers: https://answall.com/a/272376/64969

  • Related: https://answall.com/q/273728/64969

3 answers

9

Not a Python bug, but an inherent problem of how computers represent floating point.

>>> 0.1 + 0.3
0.4
>>> 0.1 + 0.2
0.30000000000000004

Fortunately, Python offers us a simple way to solve the problem: the module decimal.

>>> from decimal import Decimal
>>> Decimal('0.1') + Decimal('0.2')
Decimal('0.3')

It is important to note that using the Decimal, we should start the number as a string. Otherwise, he interprets the argument as floating point and we have the same problem:

>>> Decimal(0.1) + Decimal(0.2)
Decimal('0.3000000000000000166533453694')

That page shows the reaction of several languages to the problem.

  • 1

    Another suggestion is to leave the number in question in float even, and take care of the rolling only at the time of displaying the number (display = any output: screen, file, HTML, etc...)

1

Like Pedro said, it’s not a Python bug.

If you know that the increment will always be 0.2 (or any fixed number), you can simply store the values multiplied by 5 (the multiplicative inverse of the increment) as integrates and divide by 5.0 when you need the value as float. This avoids problems with a rounding error.


As requested, a simple example:

J1, J2, J3, I = 5, 10, 15, 0

while I <= 10:
    print( 'I={} J={}'.format(I/5.0, J1/5.0) )
    print( 'I={} J={}'.format(I/5.0, J2/5.0) )
    print( 'I={} J={}'.format(I/5.0, J3/5.0) )
    J1 += 1
    J2 += 1
    J3 += 1
    I += 1

With a result of:

I=0.0 J=1.0
I=0.0 J=2.0
I=0.0 J=3.0
I=0.2 J=1.2
I=0.2 J=2.2
I=0.2 J=3.2
I=0.4 J=1.4
I=0.4 J=2.4
I=0.4 J=3.4
I=0.6 J=1.6
I=0.6 J=2.6
I=0.6 J=3.6
I=0.8 J=1.8
I=0.8 J=2.8
I=0.8 J=3.8
I=1.0 J=2.0
I=1.0 J=3.0
I=1.0 J=4.0
I=1.2 J=2.2
I=1.2 J=3.2
I=1.2 J=4.2
I=1.4 J=2.4
I=1.4 J=3.4
I=1.4 J=4.4
I=1.6 J=2.6
I=1.6 J=3.6
I=1.6 J=4.6
I=1.8 J=2.8
I=1.8 J=3.8
I=1.8 J=4.8
I=2.0 J=3.0
I=2.0 J=4.0
I=2.0 J=5.0

It would probably be better to encapsulate the code to do division multiplication within a class, but I don’t know Python so much to give a good example of this.

Also I have heard of classes that keep fractions instead of floating points to keep (almost) any rational number without losing precision.

I think the class Decimal of Python already does something similar to what I am describing, but quarda an integro number and keeps a power of ten to make the division (or multiplication). So I guess Decimal is similar to floating point, but with powers of ten instead of powers of two.

1


While @Pedro’s response was helpful, I found the answer with examples and everything in the OS. Here is the link

My code, now working perfectly, was the following to whom it interested:

from decimal import Decimal as D

J1, J2, J3, I = D(1.0), D(2.0), D(3.0), D(0)

while I <= 2:
    print('I={} J={}'.format(I, J1))
    print('I={} J={}'.format(I, J2))
    print('I={} J={}'.format(I, J3))
    J1 += D("0.2")
    J2 += D("0.2")
    J3 += D("0.2")
    I += D("0.2")

Browser other questions tagged

You are not signed in. Login or sign up in order to post.