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.
Par exemple:
from decimal import Decimal, ROUND_HALF_UP
Decimal(1.5).quantize(0, ROUND_HALF_UP)
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
.
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.
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.
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.
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.
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
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))
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
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