Outils pour utilisateurs

Outils du site


issue79:tutoriel_-_python_49

Table des matières

1

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?!!?!?

Alors que j'étais au boulot cette semaine, une personne très sage du nom de Michael W. m'a suggéré d'examiner ce qui se passe avec des nombres à virgule flottante et l'égalité.

Prenez par exemple un simple calcul : 1.1 + 2.2

La réponse, direz-vous, est 3.3 ! Toute enfant qui a manipulé des fractions à l'école le sait. Eh bien, dites ça à votre ordinateur. Si vous démarrez le Shell Interactif Python et saisissez à l'invite (1.1+2.2) == 3.3, vous pourriez être surpris qu'il réponde « False » (faux).

QUOI ?!?!?

2

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.

Maintenant, confus, vous tapez à l'invite :

             1.1+2.2

Et la réponse est : 3.3000000000000003 Vous regardez fixement l'écran, incrédule, et pensez d'abord : « je dois avoir saisi quelque chose de travers ». Ensuite, vous vous rendez compte que non. Et vous tapez :

             2.2+3.3

5.5 Maintenant, vous êtes encore plus confus et vous vous dites : « D'accord. Il s'agit soit d'un bug soit d'une sorte d’œuf de Pâques [Ndt : blague informatique] ». Non, ce n'est ni un bug ni un œuf de Pâques. C'est réel. Même si je connaissais ce phénomène il y a très longtemps, il avait glissé dans les profondeurs et les recoins les plus sombres de mon vieux cerveau et il a fallu que je le fasse remonter. Ce que nous voyons là est la joie des nombres binaires à virgule flottante.

3

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

Nous savons tous que ⅓ vaut 0,33333333333333333… à l'infini, mais prenons par exemple la fraction 1/10. Tout le monde sait que 1/10 est égal à 0,1, non ? Si vous utilisez le shell interactif, vous pouvez voir que :

             1/10

0 Oh, c'est vrai. Nous devons avoir au moins l'une des valeurs en virgule flottante pour voir les décimales car entier/entier retourne un entier. Donc, nous essayons de nouveau.

             1/10.0

0.1

4

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?

Bon. La réalité est de retour. Non, pas vraiment. Python vous montre simplement une version arrondie de la réponse. Alors, comment voyons-nous la « vraie » réponse ? Nous pouvons utiliser la bibliothèque décimale pour voir ce qui se passe réellement.

          from decimal import *
          Decimal(1/10.0)

Decimal('0.1000000000000000055511151231257827021181583404541015625') WOW. Essayons donc notre formule originale et voyons ce que cela affiche :

          Decimal(1.1+2.2)

Decimal('3.300000000000000266453525910037569701671600341796875') Cela semble être de pire en pire. Alors qu'est-ce qui se passe réellement ?

5

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.

C'est ce qu'on appelle une Erreur de représentation, elle existe dans presque tous les langages de programmation moderne (Python, C, C++, Java, Fortran et d'autres) et sur ​​presque tous les ordinateurs modernes. C'est parce que ces machines utilisent l'arithmétique en virgule flottante IEEE-754 qui (sur la plupart des machines et des systèmes d'exploitation) correspond à un nombre en double précision IEEE-754. Ce nombre en double précision a une précision de 53 bits. Ainsi, notre 0.1, quand il est représenté en 53-bit double précision, se transforme en : 0.00011001100110011001100110011001100110011001100110011010 C'est proche de 0.1 mais pas assez proche pour éviter les problèmes.

Alors, que faisons-nous avec ça ? Eh bien, la réponse rapide est que vous pouvez probablement vivre avec dans 90% des cas que nous avons à traiter dans le monde réel - en utilisant la méthode round(). Vous devrez décider le nombre de décimales dont vous avez besoin dans votre monde pour avoir la précision dont vous avez besoin, mais la plupart du temps ce sera une solution acceptable.

6

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.

Je ne me souviens pas vraiment si nous avons vu la méthode round, donc je vais la décrire brièvement. La syntaxe est très simple : round(v,d) où v est la valeur que vous souhaitez arrondir et d est le nombre de décimales (maximum) que vous voulez après la virgule. Selon la documentation Python, « Les valeurs sont arrondies au plus proche multiple de 10 à la puissance moins n, si deux multiples sont à égale distance, l'arrondi se fait en s'écartant du 0 ». Tout cela étant dit, si le nombre est 1.4144, et que nous arrondissons à 3 décimales, la valeur retournée sera 1.414. Si le nombre est 1.4145, il serait arrondi à 1.415.

7

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)

Par exemple, utilisons la valeur de pi qui provient de la bibliothèque mathématique. (Vous devez importer la bibliothèque « math » avant de pouvoir le faire, d'ailleurs.)

          math.pi

3.141592653589793 Maintenant, si nous voulons arrondir cette valeur à 5 décimales, on peut utiliser :

          round(math.pi,5)

3.14159 C'est la valeur « standard » de pi que presque tout le monde connaît par cœur. C'est très bien. Cependant, si nous fixons le nombre de décimales à renvoyer à 4, regardez ce qui se passe.

          round(math.pi,4)

8

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!

3,1416 Tout cela fonctionne parfaitement, jusqu'à ce que vous ayez une valeur comme 2.675 et essayez de l'arrondir à 2 décimales. L'hypothèse (car on est exactement à mi-chemin entre 2,67 et 2,68), c'est que la valeur retournée sera 2.68. Essayez-le.

          round(2.675,2)

2.67 Cela pourrait poser un problème. Et on revient à la question initiale dont nous parlions. Avec la conversion en un nombre binaire à virgule flottante de 53 bits de long, le nombre devient : 2,6749999999999998223653160599749535221893310546875 qui est arrondi à 2.67.

L'essentiel ici est qu'en essayant de comparer les nombres à virgule flottante, il faut être conscient que certaines choses ne se traduisent pas bien.

Rendez-vous la prochaine fois !

issue79/tutoriel_-_python_49.txt · Dernière modification : 2014/03/29 17:57 de auntiee