Arrondir les décimales et les entiers en Python avec “round” et “Decimal.quantize

Affaires

Ce qui suit explique comment arrondir des nombres en Python en les arrondissant ou en les arrondissant à un nombre pair. Les nombres sont supposés être de type flottant (float) ou entier (int).

  • fonction intégrée (par exemple, dans un langage de programmation): round()
    • Arrondissez les décimales à n'importe quel nombre de chiffres.
    • Arrondir les nombres entiers à un nombre quelconque de chiffres.
    • round() arrondit à un nombre pair, pas à un arrondi commun
  • bibliothèque standarddecimal quantize()
    • DecimalCréation d'un objet
    • Arrondi des décimales à un nombre quelconque de chiffres et arrondi aux nombres pairs
    • Arrondi des nombres entiers à un nombre quelconque de chiffres et arrondi aux nombres pairs
  • Définir une nouvelle fonction
    • Arrondissez les décimales à n'importe quel nombre de chiffres.
    • Arrondir les nombres entiers à n'importe quel nombre de chiffres
    • Note : Pour les valeurs négatives

Notez que, comme mentionné ci-dessus, la fonction intégrée round n'est pas un arrondi général, mais un arrondi à un nombre pair. Voir ci-dessous pour plus de détails.

fonction intégrée (par exemple, dans un langage de programmation): round()

Round() est fournie comme une fonction intégrée. Elle peut être utilisée sans importer de module.

Le premier argument est le nombre original, et le second est le nombre de chiffres (le nombre de chiffres à arrondir).

Arrondissez les décimales à n'importe quel nombre de chiffres.

Voici un exemple de traitement pour le type float à virgule flottante.

Si le deuxième argument est omis, il est arrondi à un nombre entier. Le type devient également un type integer int.

f = 123.456

print(round(f))
# 123

print(type(round(f)))
# <class 'int'>

Si le second argument est spécifié, il renvoie un type de flottant.

Si un nombre entier positif est spécifié, la place décimale est spécifiée ; si un nombre entier négatif est spécifié, la place entière est spécifiée. -1 arrondit au dixième le plus proche, -2 arrondit au centième le plus proche, et 0 arrondit à un entier (la première place), mais renvoie un type flottant, contrairement à ce qui se passe lorsqu'il est omis.

print(round(f, 1))
# 123.5

print(round(f, 2))
# 123.46

print(round(f, -1))
# 120.0

print(round(f, -2))
# 100.0

print(round(f, 0))
# 123.0

print(type(round(f, 0)))
# <class 'float'>

Arrondir les nombres entiers à un nombre quelconque de chiffres.

Voici un exemple de traitement pour le type integer int.

Si le deuxième argument est omis, ou si 0 ou un nombre entier positif est spécifié, la valeur originale est retournée telle quelle. Si un nombre entier négatif est spécifié, il est arrondi au chiffre entier correspondant. Dans les deux cas, un entier de type int est retourné.

i = 99518

print(round(i))
# 99518

print(round(i, 2))
# 99518

print(round(i, -1))
# 99520

print(round(i, -2))
# 99500

print(round(i, -3))
# 100000

round() arrondit à un nombre pair, pas à un arrondi commun

Notez que l'arrondi avec la fonction intégrée round() dans Python 3 arrondit à un nombre pair, et non à un arrondi général.

Comme indiqué dans la documentation officielle, 0,5 est arrondi à 0, 5 est arrondi à 0, et ainsi de suite.

print('0.4 =>', round(0.4))
print('0.5 =>', round(0.5))
print('0.6 =>', round(0.6))
# 0.4 => 0
# 0.5 => 0
# 0.6 => 1

print('4 =>', round(4, -1))
print('5 =>', round(5, -1))
print('6 =>', round(6, -1))
# 4 => 0
# 5 => 0
# 6 => 10

La définition de l'arrondi à un nombre pair est la suivante.

Si la fraction est inférieure à 0,5, arrondissez-la au chiffre inférieur ; si la fraction est supérieure à 0,5, arrondissez-la au chiffre supérieur ; si la fraction est exactement 0,5, arrondissez-la au chiffre pair entre l'arrondi au chiffre inférieur et l'arrondi au chiffre supérieur.
Rounding – Wikipedia

0,5 n'est pas toujours tronqué.

print('0.5 =>', round(0.5))
print('1.5 =>', round(1.5))
print('2.5 =>', round(2.5))
print('3.5 =>', round(3.5))
print('4.5 =>', round(4.5))
# 0.5 => 0
# 1.5 => 2
# 2.5 => 2
# 3.5 => 4
# 4.5 => 4

Dans certains cas, la définition de l'arrondi à un nombre pair ne s'applique même pas au traitement après deux décimales.

print('0.05 =>', round(0.05, 1))
print('0.15 =>', round(0.15, 1))
print('0.25 =>', round(0.25, 1))
print('0.35 =>', round(0.35, 1))
print('0.45 =>', round(0.45, 1))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

Cela est dû au fait que les décimales ne peuvent pas être représentées exactement comme des nombres à virgule flottante, comme indiqué dans la documentation officielle.

Le comportement de round() pour les nombres à virgule flottante peut vous surprendre.:Par exemple, round(2.675, 2) vous donnera 2.67 au lieu de 2.68 comme prévu. Il ne s'agit pas d'un bogue.:Ceci est dû au fait que la plupart des décimales ne peuvent pas être représentées exactement par des nombres à virgule flottante.
round() — Built-in Functions — Python 3.10.2 Documentation

Si vous souhaitez obtenir un arrondi général ou un arrondi précis des décimales aux nombres pairs, vous pouvez utiliser la bibliothèque standard decimal quantize (décrite ci-dessous) ou définir une nouvelle fonction.

Notez également que round() dans Python 2 n'est pas un arrondi à un nombre pair, mais un arrondi.

quantize() de la bibliothèque standard décimale

Le module décimal de la bibliothèque standard peut être utilisé pour manipuler des nombres décimaux exacts à virgule flottante.

En utilisant la méthode quantize() du module décimal, il est possible d'arrondir les nombres en spécifiant le mode d'arrondi.

Les valeurs définies pour l'arrondi de l'argument de la méthode quantize() ont les significations suivantes, respectivement.

  • ROUND_HALF_UP:Arrondi général
  • ROUND_HALF_EVEN:Arrondir aux nombres pairs

Le module decimal est une bibliothèque standard, donc aucune installation supplémentaire n'est requise, mais l'importation est nécessaire.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

Création d'un objet Décimal

Decimal() peut être utilisé pour créer des objets de type Decimal.

Si vous spécifiez un type de flottant comme argument, vous pouvez voir comment la valeur est réellement traitée.

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

print(type(Decimal(0.05)))
# <class 'decimal.Decimal'>

Comme le montre l'exemple, 0,05 n'est pas traité comme exactement 0,05. C'est la raison pour laquelle la fonction intégrée round() décrite ci-dessus arrondit à une valeur différente de celle attendue pour les valeurs décimales incluant 0,05 dans l'exemple.

Comme 0,5 est la moitié (-1 puissance de 2), il peut être exprimé exactement en notation binaire.

print(Decimal(0.5))
# 0.5

Si vous spécifiez le type de chaîne str au lieu du type float, il sera traité comme le type Decimal de la valeur exacte.

print(Decimal('0.05'))
# 0.05

Arrondi des décimales à un nombre quelconque de chiffres et arrondi aux nombres pairs

Appelez quantize() à partir d'un objet de type Decimal pour arrondir la valeur.

Le premier argument de quantize() est une chaîne de caractères avec le même nombre de chiffres que le nombre de chiffres que vous voulez trouver, comme '0.1' ou '0.01'.

De plus, l'argument ROUNDING spécifie le mode d'arrondi ; si ROUND_HALF_UP est spécifié, l'arrondi général est utilisé.

f = 123.456

print(Decimal(str(f)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 123

print(Decimal(str(f)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP))
# 123.5

print(Decimal(str(f)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 123.46

Contrairement à la fonction intégrée round(), 0,5 est arrondi à 1.

print('0.4 =>', Decimal(str(0.4)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
print('0.5 =>', Decimal(str(0.5)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
print('0.6 =>', Decimal(str(0.6)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 0.4 => 0
# 0.5 => 1
# 0.6 => 1

Si l'argument rounding est défini sur ROUND_HALF_EVEN, l'arrondi est effectué sur les nombres pairs comme dans la fonction intégrée round().

Comme mentionné ci-dessus, si un type de flottant est spécifié comme argument de Decimal(), il est traité comme un objet Decimal avec une valeur égale à la valeur réelle du type de flottant, donc le résultat de l'utilisation de la méthode quantize() sera différent de ce qui est attendu, tout comme la fonction intégrée round().

print('0.05 =>', round(0.05, 1))
print('0.15 =>', round(0.15, 1))
print('0.25 =>', round(0.25, 1))
print('0.35 =>', round(0.35, 1))
print('0.45 =>', round(0.45, 1))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

print('0.05 =>', Decimal(0.05).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.15 =>', Decimal(0.15).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.25 =>', Decimal(0.25).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.35 =>', Decimal(0.35).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.45 =>', Decimal(0.45).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

Si l'argument de Decimal() est spécifié comme une chaîne de caractères de type str, il est traité comme un objet Decimal ayant exactement cette valeur, et le résultat est donc conforme aux attentes.

print('0.05 =>', Decimal(str(0.05)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.15 =>', Decimal(str(0.15)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.25 =>', Decimal(str(0.25)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.35 =>', Decimal(str(0.35)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.45 =>', Decimal(str(0.45)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
# 0.05 => 0.0
# 0.15 => 0.2
# 0.25 => 0.2
# 0.35 => 0.4
# 0.45 => 0.4

Étant donné que 0,5 peut être correctement traité par le type float, il n'y a aucun problème à spécifier le type float comme argument de Decimal() lors de l'arrondi à un entier, mais il est plus sûr de spécifier le type string str lors de l'arrondi à une décimale.

Par exemple, 2,675 est en fait 2,67499…. dans le type float. Par conséquent, si vous voulez arrondir à deux décimales, vous devez spécifier une chaîne de caractères à Decimal(), sinon le résultat sera différent du résultat attendu, que vous arrondissiez au nombre entier le plus proche (ROUND_HALF_UP) ou à un nombre pair (ROUND_HALF_EVEN).

print(Decimal(2.675))
# 2.67499999999999982236431605997495353221893310546875

print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.67

print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.68

print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.67

print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.68

Notez que la méthode quantize() renvoie un nombre de type décimal, donc si vous voulez opérer sur un nombre de type flottant, vous devez le convertir en type flottant en utilisant float(), sinon une erreur se produira.

d = Decimal('123.456').quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)

print(d)
# 123.46

print(type(d))
# <class 'decimal.Decimal'>

# print(1.2 + d)
# TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal'

print(1.2 + float(d))
# 124.66

Arrondi des nombres entiers à un nombre quelconque de chiffres et arrondi aux nombres pairs

Si vous voulez arrondir à un chiffre entier, spécifier quelque chose comme '10' comme premier argument ne vous donnera pas le résultat souhaité.

i = 99518

print(Decimal(i).quantize(Decimal('10'), rounding=ROUND_HALF_UP))
# 99518

C'est parce que quantize() effectue l'arrondi en fonction de l'exposant de l'objet Decimal, mais l'exposant de Decimal('10') est 0, et non 1.

Vous pouvez spécifier un exposant arbitraire en utilisant E comme chaîne d'exposant (par exemple, '1E1'). L'exposant peut être vérifié dans la méthode as_tuple.

print(Decimal('10').as_tuple())
# DecimalTuple(sign=0, digits=(1, 0), exponent=0)

print(Decimal('1E1').as_tuple())
# DecimalTuple(sign=0, digits=(1,), exponent=1)

Comme c'est le cas, le résultat sera en notation exponentielle en utilisant E. Si vous voulez utiliser la notation normale, ou si vous voulez opérer avec le type entier int après l'arrondi, utilisez int() pour convertir le résultat.

print(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))
# 9.952E+4

print(int(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
# 99520

print(int(Decimal(i).quantize(Decimal('1E2'), rounding=ROUND_HALF_UP)))
# 99500

print(int(Decimal(i).quantize(Decimal('1E3'), rounding=ROUND_HALF_UP)))
# 100000

Si l'argument rounding est défini sur ROUND_HALF_UP, un arrondi général sera effectué, par exemple, 5 sera arrondi à 10.

print('4 =>', int(Decimal(4).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
print('5 =>', int(Decimal(5).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
print('6 =>', int(Decimal(6).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
# 4 => 0
# 5 => 10
# 6 => 10

Bien entendu, il n'y a aucun problème si vous le spécifiez sous forme de chaîne de caractères.

Définir une nouvelle fonction

La méthode qui consiste à utiliser le module décimal est précise et sûre, mais si vous n'êtes pas à l'aise avec la conversion de type, vous pouvez définir une nouvelle fonction pour réaliser un arrondi général.

Il existe de nombreuses façons de procéder, par exemple, la fonction suivante.

def my_round(val, digit=0):
    p = 10 ** digit
    return (val * p * 2 + 1) // 2 / p

Si vous n'avez pas besoin de préciser le nombre de chiffres et que vous arrondissez toujours à la première décimale, vous pouvez utiliser une forme plus simple.

my_round_int = lambda x: int((x * 2 + 1) // 2)

Si vous devez être précis, il est plus sûr d'utiliser les décimales.

Les informations suivantes sont données à titre indicatif.

Arrondissez les décimales à n'importe quel nombre de chiffres.

print(int(my_round(f)))
# 123

print(my_round_int(f))
# 123

print(my_round(f, 1))
# 123.5

print(my_round(f, 2))
# 123.46

Contrairement à l'arrondi, 0,5 devient 1 selon l'arrondi général.

print(int(my_round(0.4)))
print(int(my_round(0.5)))
print(int(my_round(0.6)))
# 0
# 1
# 1

Arrondir les nombres entiers à n'importe quel nombre de chiffres

i = 99518

print(int(my_round(i, -1)))
# 99520

print(int(my_round(i, -2)))
# 99500

print(int(my_round(i, -3)))
# 100000

Contrairement à l'arrondi, 5 devient 10 selon l'arrondi courant.

print(int(my_round(4, -1)))
print(int(my_round(5, -1)))
print(int(my_round(6, -1)))
# 0
# 10
# 10

Note : Pour les valeurs négatives

Dans l'exemple de fonction ci-dessus, -0,5 est arrondi à 0.

print(int(my_round(-0.4)))
print(int(my_round(-0.5)))
print(int(my_round(-0.6)))
# 0
# 0
# -1

Il existe plusieurs façons d'envisager l'arrondi pour les valeurs négatives, mais si vous voulez transformer -0,5 en -1, vous pouvez le modifier comme suit, par exemple

import math

def my_round2(val, digit=0):
    p = 10 ** digit
    s = math.copysign(1, val)
    return (s * val * p * 2 + 1) // 2 / p * s

print(int(my_round2(-0.4)))
print(int(my_round2(-0.5)))
print(int(my_round2(-0.6)))
# 0
# -1
# -1
Copied title and URL