Outils pour utilisateurs

Outils du site


issue79:tutoriel_-_python_47

Programming in Python - Part 49 By G.D. Walters While I was working this week, a very wise person by the name of Michael W. suggested that I should consider what happens with floating-point numbers and equality.

Take for example a simple calculation: 1.1 + 2.2

The answer, you say, is 3.3! Any school-kid who has dealt with fractions knows that. Well, tell your computer. If you start up the Python Interactive Shell and at the prompt type (1.1+2.2) == 3.3, you might be surprised that the shell responds “False”.

WHAT?!!?!?

Now, confused, you type at the prompt:

1.1+2.2

And the shell responds back: 3.3000000000000003 You stare at the screen in disbelief and first think “I must have typed something wrong”. Then you realize that you didn’t. So you type:

2.2+3.3

5.5 Now you are even more confused and you think to yourself “Ok. This is either a bug or some kind of sick Easter egg.” No, it’s neither a bug nor an Easter egg. It’s real. While I knew about this a very long time ago, it had slipped into the cobwebs hidden in the dark recesses of my old mind, so I had to bring it up here. What we are seeing is the joy of binary floating-point numbers.

We all know that ⅓ equates to .33333333333333333… for ever and a day, but take, for example, the fraction 1/10. Everyone knows that 1/10 is equal to .1, right? If you use the interactive shell you can see that:

1/10

0 Oh, right. We have to have at least one of the values a floating-point value to show any decimal points since an integer/integer returns an integer. So we try again.

1/10.0

0.1

Ok. Reality is back. No, not really. Python is simply showing you a rounded version of the answer. So, how do we see the “real” answer? We can use the decimal library to see what’s really happening.

from decimal import *
Decimal(1/10.0)

Decimal('0.1000000000000000055511151231257827021181583404541015625') WOW. So let’s try our original formula and see what that would show:

Decimal(1.1+2.2)

Decimal('3.300000000000000266453525910037569701671600341796875') It seems to just be getting worse and worse. So what is really happening?

This is called Representation Error, and exists in almost every modern programming language (Python, C, C++, Java, and even Fortran and more), and on almost every modern computer. This is because these machines use IEEE-754 floating-point arithmetic which (on most machines and OS platforms) maps to an IEEE-754 double-precision number. This double-precision number has a precision of 53 bits. So, our 0.1, when represented in this 53-bit double-precision, turns into: 0.00011001100110011001100110011001100110011001100110011010 That’s close to .1, but not close enough to avoid issues.

So what do we do about it? Well, the quick answer is that you probably can live with it for 90% of the things we have to do out there in the real world – by using the round() method. While you have to decide on the number of decimal points that you must have in your world to carry the precision that you need, for the most part, this will be an acceptable workaround.

I honestly don’t remember if we have gone over the round method, so I’ll briefly go over it. The syntax is very simple: round(v,d) where v is the value you want to round and d is the number of decimals (maximum) you want after the decimal point. According to the Python documentation, “Values are rounded to the closest multiple of 10 to the power of minus n digits; if two multiples are equally close, rounding is done away from 0”. All that being said, if the number is 1.4144, and we round it to 3 decimal places, the returned value will be 1.414. If the number is 1.4145 it would be returned as 1.415.

For example, let’s use the value of pi that comes from the math library. (You must import the math library before you can do this, by the way.)

math.pi

3.141592653589793 Now, if we wanted to round that value down to 5 decimal places, we would use:

round(math.pi,5)

3.14159 That is the “standard” value of pi that most everyone knows off the top of their head. That’s great. However, if we set the number of decimal places to be returned to 4, look what happens.

round(math.pi,4)

3.1416 All that sounds good until you run into a value like 2.675 and try to round it to 2 decimal places. The assumption (since it is exactly halfway between 2.67 and 2.68) is that the returned value will be 2.68. Try it.

round(2.675,2)

2.67 That might cause a problem. It goes back to the initial issue we have been talking about. The actual conversion to a binary floating-point number that is 53 bits long, the number becomes: 2.6749999999999998223653160599749535221893310546875 which then rounds down to 2.67.

The bottom line here is when trying to compare floating-point numbers, be aware that some things just don’t translate well.

See you next time!

issue79/tutoriel_-_python_47.txt · Dernière modification : 2014/01/21 14:36 de andre_domenech