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.

30
demandé sur Delgan 2015-10-08 18:11:43

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
25
répondu Martijn Pieters 2018-06-13 16:17:28

Par exemple:

from decimal import Decimal, ROUND_HALF_UP

Decimal(1.5).quantize(0, ROUND_HALF_UP)
18
répondu dhobbs 2015-10-08 15:28:57

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.

11
répondu Matthew D. Scholefield 2018-04-10 16:09:30

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.

8
répondu fedor2612 2016-12-18 07:20:49

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.

4
répondu Patricia Shanahan 2015-10-08 15:24:35

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.

3
répondu rmunn 2015-10-08 15:20:21

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.

1
répondu MRocklin 2015-10-08 15:19:46

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))
0
répondu cdonts 2015-10-08 15:29:43

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

0
répondu Joe Cat 2018-10-02 23:54:38