Comment arrondir correctement les demi-nombres en Python?
je suis confronté à un comportement étrange de la round()
fonction:
for i in range(1, 15, 2):
n = i / 2
print(n, "=>", round(n))
ce code imprime:
0.5 => 0
1.5 => 2
2.5 => 2
3.5 => 4
4.5 => 4
5.5 => 6
6.5 => 6
Je m'attendais à ce que les valeurs flottantes soient toujours arrondies vers le haut, mais à la place, elles sont arrondies au nombre pair le plus proche.
pourquoi un tel comportement, et quelle est la meilleure façon d'obtenir le bon résultat?
j'ai essayé d'utiliser le fractions
mais le résultat est le même.
9 réponses
Types Numériques documente explicitement ce comportement:
round(x[, n])
x arrondi à n chiffres, l'arrondissement demi-même. Si n est omis, la valeur par défaut est 0.
Notez le arrondir à la moitié égale. Il est également appelé les banquiers arrondi; au lieu de toujours arrondir vers le haut ou vers le bas (en aggravant les erreurs d'arrondissement), en arrondissant au chiffre le plus proche même nombre d'erreurs moyennes d'arrondissement.
si vous avez besoin de plus de contrôle sur le comportement d'arrondi, utilisez le decimal
module, qui vous permet de spécifier exactement ce que il faut utiliser une stratégie d'arrondissement.
par exemple, pour arrondir de la moitié:
>>> from decimal import localcontext, Decimal, ROUND_HALF_UP
>>> with localcontext() as ctx:
... ctx.rounding = ROUND_HALF_UP
... for i in range(1, 15, 2):
... n = Decimal(i) / 2
... print(n, '=>', n.to_integral_value())
...
0.5 => 1
1.5 => 2
2.5 => 3
3.5 => 4
4.5 => 5
5.5 => 6
6.5 => 7
Par exemple:
from decimal import Decimal, ROUND_HALF_UP
Decimal(1.5).quantize(0, ROUND_HALF_UP)
round()
arrondira vers le haut ou vers le bas, selon que le nombre est pair ou impair. Une façon simple de ne faire qu'un tour d'horizon est:
int(num + 0.5)
Si vous voulez que cela fonctionne correctement pour les nombres négatifs utilisation:
((num > 0) - (num < 0)) * int(abs(num) + 0.5)
Remarque, cela peut gâcher pour les grands nombres ou chiffres précis comme 5000000000000001.0
et 0.49999999999999994
.
Vous pouvez utiliser ceci:
import math
def normal_round(n):
if n - math.floor(n) < 0.5:
return math.floor(n)
return math.ceil(n)
il va arrondir le nombre vers le haut ou vers le bas correctement.
le comportement que vous voyez est typique IEEE 754 comportement d'arrondi. S'il doit choisir entre deux nombres qui sont également différents de l'entrée, il choisit toujours le même. L'avantage de ce comportement est que l'effet d'arrondissement moyen est zéro - autant de nombres arrondis vers le haut et vers le bas. Si vous contournez les nombres à mi-chemin dans une direction cohérente, l'arrondissement affectera la valeur attendue.
Le problème que vous rencontrez est correcte si l'objectif est juste l'arrondissement, mais ce n'est pas toujours ce qui est nécessaire.
Une astuce pour obtenir le type d'arrondi que vous voulez est pour ajouter 0,5 et ensuite prendre la parole. Par exemple, ajouter 0.5 à 2.5 donne 3, avec le plancher 3.
version Courte: module décimal. Il peut représenter des nombres comme 2.675 précisément, contrairement aux flotteurs Python où 2.675 est vraiment 2.67499999999999982236431605997495353221893310546875 (exactement). Et vous pouvez spécifier l'arrondissement que vous désirez: ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP, et ROUND_05UP sont toutes les options.
L'arrondi au nombre pair le plus proche est devenu pratique courante dans les disciplines numériques. "Arrondi" produit un léger biais vers plus de résultats.
Donc, du point de vue de l'establishment scientifique, round
a le comportement correct.
Vous pouvez utiliser:
from decimal import Decimal, ROUND_HALF_UP
for i in range(1, 15, 2):
n = i / 2
print(n, "=>", Decimal(str(n)).quantize(Decimal("1"), rounding=ROUND_HALF_UP))
Amour fedor2612 répondre. J'ai agrandi avec une option "décimales" l'argument pour ceux qui veulent utiliser cette fonction pour arrondir un nombre de décimales (disons, par exemple, si vous voulez arrondir un bureau de $26.455 $26.46).
import math
def normal_round(n, decimals=0):
expoN = n * 10 ** decimals
if abs(expoN) - abs(math.floor(expoN)) < 0.5:
return math.floor(expoN) / 10 ** decimals
return math.ceil(expoN) / 10 ** decimals
oldRounding = round(26.455,2)
newRounding = normal_round(26.455,2)
print(oldRounding)
print(newRounding)
Sortie:
26.45
26.46