Je me sens obligé d'apporter un contrepoint à la réponse d'Ashwini Chaudhary. Malgré les apparences, la forme à deux arguments de la round
fonction n'est pas arrondir un flottant Python à un nombre donné de décimales, et ce n'est souvent pas la solution que vous souhaitez, même lorsque vous pensez que c'est le cas. Laissez-moi vous expliquer...
La possibilité d'arrondir un flottant (Python) à un certain nombre de décimales est quelque chose de fréquemment demandé, mais qui s'avère être rarement ce dont on a réellement besoin. La réponse est d'une simplicité déconcertante round(x, number_of_places)
est une sorte de nuisance attrayante : elle regarde comme s'il faisait ce que vous voulez, mais grâce au fait que les flottants Python sont stockés en interne en binaire, il fait quelque chose de plus subtil. Prenons l'exemple suivant :
>>> round(52.15, 1)
52.1
Avec une compréhension naïve de ce que round
mais cela ne semble pas correct : il faudrait sûrement arrondir en haut a 52.2
plutôt que en bas a 52.1
? Pour comprendre pourquoi on ne peut pas se fier à de tels comportements, il faut savoir que si cette opération ressemble à une simple opération de décimal à décimal, elle est loin d'être simple.
Donc, voici ce qui est vraiment dans l'exemple ci-dessus. ( une profonde respiration ) Nous affichons un décimal représentation du plus proche binaire nombre à virgule flottante au plus proche n
-digits-after-the-point décimal à un numéro binaire approximation en virgule flottante d'un littéral numérique écrit en décimal . Ainsi, pour passer du littéral numérique d'origine à la sortie affichée, la machinerie sous-jacente a fait quatre des conversions séparées entre les formats binaire et décimal, deux dans chaque sens. En décomposant (et avec les avertissements habituels concernant l'hypothèse du format binaire IEEE 754, l'arrondi à la paire, et les règles IEEE 754) :
-
D'abord le littéral numérique 52.15
est analysé et converti en un flottant Python. Le nombre réel stocké est 7339460017730355 * 2**-47
ou 52.14999999999999857891452847979962825775146484375
.
-
En interne, comme première étape de la round
Python calcule la chaîne décimale à un chiffre après la virgule la plus proche du nombre stocké. Puisque ce nombre stocké est un peu en dessous de la valeur originale de 52.15
on finit par arrondir à la baisse et obtenir une chaîne de caractères 52.1
. Cela explique pourquoi nous avons 52.1
comme sortie finale au lieu de 52.2
.
-
Ensuite, dans la deuxième étape de la round
Python retransforme cette chaîne en flottant, en obtenant le nombre binaire à virgule flottante le plus proche de 52.1
qui est maintenant 7332423143312589 * 2**-47
ou 52.10000000000000142108547152020037174224853515625
.
-
Enfin, dans le cadre de la boucle de lecture-évaluation-impression (REPL) de Python, la valeur en virgule flottante est affichée (en décimal). Cela implique de reconvertir la valeur binaire en une chaîne décimale, d'obtenir 52.1
comme résultat final.
Dans Python 2.7 et plus, nous avons l'agréable situation que les deux conversions des étapes 3 et 4 s'annulent. Cela est dû au choix par Python de l'option repr
qui produit la valeur décimale la plus courte garantissant un arrondi correct à la valeur flottante réelle. Une conséquence de ce choix est que si vous commencez avec une valeur décimale (ni trop grande, ni trop petite) comportant 15 chiffres significatifs ou moins, la valeur flottante correspondante sera affichée avec ces mêmes chiffres :
>>> x = 15.34509809234
>>> x
15.34509809234
Malheureusement, cela renforce l'illusion que Python stocke les valeurs en décimal. Ce n'est pas le cas dans Python 2.6 ! Voici l'exemple original exécuté en Python 2.6 :
>>> round(52.15, 1)
52.200000000000003
Non seulement nous tournons dans la direction opposée, en obtenant 52.2
au lieu de 52.1
mais la valeur affichée ne s'imprime même pas en tant que 52.2
! Ce comportement a donné lieu à de nombreux rapports sur le système de suivi des bogues de Python, du type "round is broken !". Mais ce n'est pas le cas round
qui est cassé, ce sont les attentes des utilisateurs. (Ok, ok, round
es un petit un peu cassé dans Python 2.6, dans la mesure où il n'utilise pas d'arrondis corrects).
Version courte : si vous utilisez un tour à deux arguments, et que vous attendez un comportement prévisible de la part d'une binaire approximation d'un décimal tour d'un binaire approximation d'un décimal à mi-chemin, vous cherchez les ennuis.
Alors assez avec l'argument "le tour à deux arguments est mauvais". Qu'est-ce que devrait que vous utilisez à la place ? Il y a plusieurs possibilités, en fonction de ce que vous essayez de faire.
-
Si vous arrondissez à des fins d'affichage, vous ne voulez pas du tout d'un résultat à virgule flottante, mais d'une chaîne de caractères. Dans ce cas, la solution consiste à utiliser le formatage des chaînes de caractères :
>>> format(66.66666666666, '.4f')
'66.6667'
>>> format(1.29578293, '.6f')
'1.295783'
Même dans ce cas, il faut être conscient de la représentation binaire interne pour ne pas être surpris par le comportement des demi-cas décimaux apparents.
>>> format(52.15, '.1f')
'52.1'
-
Si vous travaillez dans un contexte où il est important de savoir dans quel sens les demi-cadres décimaux sont arrondis (par exemple, dans certains contextes financiers), vous voudrez peut-être représenter vos nombres à l'aide du symbole Decimal
type. En effectuant un arrondi décimal sur le Decimal
a beaucoup plus de sens que sur un type binaire (de même, l'arrondi à un nombre fixe de positions binaires est parfaitement logique sur un type binaire). De plus, le decimal
vous permet de mieux contrôler le mode d'arrondi. En Python 3, round
fait directement le travail. En Python 2, vous avez besoin de l'option quantize
méthode.
>>> Decimal('66.66666666666').quantize(Decimal('1e-4'))
Decimal('66.6667')
>>> Decimal('1.29578293').quantize(Decimal('1e-6'))
Decimal('1.295783')
-
Dans de rares cas, la version à deux arguments de l'option round
vraiment es ce que vous voulez : peut-être que vous regroupez des flottants dans des bacs de taille 0.01
et vous ne vous souciez pas particulièrement de l'issue des affaires frontalières. Cependant, ces cas sont rares, et il est difficile de justifier l'existence de la version à deux arguments de l'option round
builtin basé sur ces seuls cas.