web-dev-qa-db-fra.com

Comment arrondir correctement les demi-nombres flottants en Python?

Je suis confronté à un comportement étrange de la fonction round():

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, mais au contraire, elles sont arrondies au nombre pair le plus proche.

Pourquoi un tel comportement et quel est le meilleur moyen d’obtenir le bon résultat?

J'ai essayé d'utiliser le fractions mais le résultat est le même.

33
Delgan

Par exemple:

from decimal import Decimal, ROUND_HALF_UP

Decimal(1.5).quantize(0, ROUND_HALF_UP)
21
dhobbs

round() arrondira le nombre de fois supérieur ou inférieur, selon que le nombre est pair ou impair Une façon simple d'arrondir consiste à:

int(num + 0.5)

Si vous voulez que cela fonctionne correctement pour les nombres négatifs, utilisez:

((num > 0) - (num < 0)) * int(abs(num) + 0.5)

Notez que cela peut gâcher de gros nombres ou des nombres très précis comme 5000000000000001.0 et 0.49999999999999994.

12

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 arrondira correctement le nombre.

9
fedor2612

Le comportement que vous observez correspond au comportement d’arrondi IEEE 754. S'il doit choisir entre deux nombres également différents de l'entrée, il choisit toujours le nombre pair. L'avantage de ce comportement est que l'effet d'arrondi moyen est égal à zéro - autant de nombres arrondis. Si vous arrondissez les numéros à mi-parcours dans une direction cohérente, l'arrondi aura une incidence sur la valeur attendue.

Le comportement que vous observez est correct si l'objectif est d'arrondi bien, mais ce n'est pas toujours ce qui est nécessaire.

Une astuce pour obtenir le type d'arrondi que vous souhaitez consiste à ajouter 0,5, puis à prendre la parole. Par exemple, en ajoutant 0,5 à 2,5, vous obtenez 3, avec le sol 3.

4

Version courte: utilisez le module décimal . Il peut représenter des nombres tels que 2.675 avec précision, contrairement aux flottants Python où 2.675 est vraiment 2.67499999999999982236431605997495353221893310546875 (exactement). Et vous pouvez spécifier l'arrondi souhaité: ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP et ROUND_05UP sont toutes des options.

3
rmunn

Arrondir au nombre pair le plus proche est devenu une pratique courante dans les disciplines numériques. "Arrondir" produit un léger biais en faveur de résultats plus importants.

Donc, du point de vue de l’établissement scientifique, round a le comportement correct.

1
MRocklin

La solution suivante a abouti à un "arrondi à la mode scolaire" sans utiliser le module decimal (qui s'avère être lent).

def school_round(a_in,n_in):
''' python uses "banking round; while this round 0.05 up" '''
    if (a_in * 10 ** (n_in + 1)) % 10 == 5:
        return round(a_in + 1 / 10 ** (n_in + 1), n_in)
    else:
        return round(a_in, n_in)

par exemple. 

print(round(0.005,2)) # 0
print(school_round(0.005,2)) #0.01
0
Yuchen Peng

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
cdonts

Love the fedor2612 réponse. Je l'ai développé avec un argument facultatif "décimales" pour ceux qui souhaitent utiliser cette fonction pour arrondir un nombre quelconque de décimales (par exemple, si vous souhaitez arrondir une devise 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
Joe Cat

Un arrondi mathématique classique sans aucune bibliothèque

def rd(x,y=0):
''' A classical mathematical rounding by Voznica '''
m = int('1'+'0'*y) # multiplier - how many positions to the right
q = x*m # shift to the right by multiplier
c = int(q) # new number
i = int( (q-c)*10 ) # indicator number on the right
if i >= 5:
    c += 1
return c/m

Compare:

print( round(0.49), round(0.51), round(0.5), round(1.5), round(2.5), round(0.15,1))  # 0  1  0  2  2  0.1

print( rd(0.49), rd(0.51), rd(0.5), rd(1.5), rd(2.5), rd(0.15,1))  # 0  1  1  2  3  0.2
0
qa13