Je lisais simplement What's New In Python 3.0 et il est écrit:
La stratégie d'arrondi de la fonction round () et le type de retour ont changé. Les cas à mi-chemin exacts sont maintenant arrondis au résultat pair le plus proche au lieu d'être à l'abri de zéro. (Par exemple, le tour (2.5) renvoie maintenant 2 plutôt que 3.)
et la documentation pour round :
Pour les types intégrés prenant en charge round (), les valeurs sont arrondies au multiple de 10 le plus proche de la puissance moins n; si deux multiples sont égaux, l'arrondi est fait vers le même choix
Donc, sous v2.7.3 :
In [85]: round(2.5)
Out[85]: 3.0
In [86]: round(3.5)
Out[86]: 4.0
comme je l'aurais prévu. Cependant, maintenant sous v3.2.3 :
In [32]: round(2.5)
Out[32]: 2
In [33]: round(3.5)
Out[33]: 4
Cela semble contre-intuitif et contraire à ce que je comprends de l'arrondi (et au risque de faire trébucher les gens). L'anglais n'est pas ma langue maternelle, mais jusqu'à ce que je lise ceci, je pensais savoir ce que signifiait arrondir: -/Je suis sûr qu'au moment où la v3 a été introduite, il devait y avoir une discussion à ce sujet, mais je n'ai pas trouvé de bonne raison de ma recherche.
Qu'est-ce que j'oublie ici?
MISE À JOUR: Le commentaire de @ Li-aungYip sur "L'arrondissement bancaire" m'a donné le bon terme de recherche/mots clés à rechercher et j'ai trouvé ceci SO question: Pourquoi .Net utilise-t-il l'arrondi bancaire par défaut? , je vais donc lire cela attentivement.
La méthode Python 3.0 est considérée comme la méthode d'arrondissement standard de nos jours, bien que certaines implémentations de langage ne soient pas encore sur le bus.
La technique simple "toujours arrondi à 0,5" produit un léger biais en faveur du nombre le plus élevé. Avec un grand nombre de calculs, cela peut être important. L'approche Python 3.0 élimine ce problème.
Il y a plus d'une méthode d'arrondissement couramment utilisée. IEEE 754, la norme internationale pour les calculs en virgule flottante, définit cinq méthodes d'arrondi différentes (celle utilisée par Python 3.0 est la méthode par défaut). Et il y en a d'autres.
Ce comportement n'est pas aussi connu qu'il devrait l'être. AppleScript a été, si je me souviens bien, un des premiers à adopter cette méthode d’arrondi. La commande round
dans AppleScript offre en fait plusieurs options, mais la valeur par défaut est identique à celle utilisée dans IEEE 754. Apparemment, l’ingénieur qui a implémenté la commande round
en a marre de toutes les demandes visant à le "faire fonctionner de la même manière" appris à l'école "qu'il a mis en œuvre que: round 2.5 rounding as taught in school
est une commande AppleScript valide. :-)
Vous pouvez contrôler l’arrondi obtenu dans Py3000 à l’aide du module Décimal :
>>> decimal.Decimal('3.5').quantize(decimal.Decimal('1'),
rounding=decimal.ROUND_HALF_UP)
>>> Decimal('4')
>>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'),
rounding=decimal.ROUND_HALF_EVEN)
>>> Decimal('2')
>>> decimal.Decimal('3.5').quantize(decimal.Decimal('1'),
rounding=decimal.ROUND_HALF_DOWN)
>>> Decimal('3')
Juste pour ajouter ici une note importante de la documentation:
https://docs.python.org/dev/library/functions.html#round
Remarque
Le comportement de round () pour les floats peut être surprenant: par exemple, tour (2,675, 2) donne 2,67 au lieu des 2,68 attendus. Ceci n'est pas un bug: c’est le résultat du fait que la plupart des fractions décimales ne peuvent pas être représenté exactement comme un flotteur. Voir Arithmétique en virgule flottante: problèmes et Limitations pour plus d'informations.
Alors ne soyez pas surpris d'obtenir les résultats suivants en Python 3.2:
>>> round(0.25,1), round(0.35,1), round(0.45,1), round(0.55,1)
(0.2, 0.3, 0.5, 0.6)
>>> round(0.025,2), round(0.035,2), round(0.045,2), round(0.055,2)
(0.03, 0.04, 0.04, 0.06)
J'ai récemment eu des problèmes avec cela aussi. Par conséquent, j'ai développé un module python 3 qui a 2 fonctions, trueround () et trueround_precision (), qui répondent à cela et donnent le même comportement d'arrondi que celui utilisé depuis l'école primaire (pas l'arrondi de banquier). Voici le module. Sauvegardez simplement le code et copiez-le ou importez-le. Remarque: le module trueround_precision peut modifier le comportement d'arrondi en fonction des besoins en fonction des paramètres ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN (voir cette documentation) Pour les fonctions ci-dessous, consultez la documentation ou utilisez help (trueround) et help (trueround_precision) si elles sont copiées dans un interpréteur pour plus de documentation.
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
def trueround(number, places=0):
'''
trueround(number, places)
example:
>>> trueround(2.55, 1) == 2.6
True
uses standard functions with no import to give "normal" behavior to
rounding so that trueround(2.5) == 3, trueround(3.5) == 4,
trueround(4.5) == 5, etc. Use with caution, however. This still has
the same problem with floating point math. The return object will
be type int if places=0 or a float if places=>1.
number is the floating point number needed rounding
places is the number of decimal places to round to with '0' as the
default which will actually return our interger. Otherwise, a
floating point will be returned to the given decimal place.
Note: Use trueround_precision() if true precision with
floats is needed
GPL 2.0
copywrite by Narnie Harshoe <[email protected]>
'''
place = 10**(places)
rounded = (int(number*place + 0.5if number>=0 else -0.5))/place
if rounded == int(rounded):
rounded = int(rounded)
return rounded
def trueround_precision(number, places=0, rounding=None):
'''
trueround_precision(number, places, rounding=ROUND_HALF_UP)
Uses true precision for floating numbers using the 'decimal' module in
python and assumes the module has already been imported before calling
this function. The return object is of type Decimal.
All rounding options are available from the decimal module including
ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN,
ROUND_HALF_UP, ROUND_UP, and ROUND_05UP.
examples:
>>> trueround(2.5, 0) == Decimal('3')
True
>>> trueround(2.5, 0, ROUND_DOWN) == Decimal('2')
True
number is a floating point number or a string type containing a number on
on which to be acted.
places is the number of decimal places to round to with '0' as the default.
Note: if type float is passed as the first argument to the function, it
will first be converted to a str type for correct rounding.
GPL 2.0
copywrite by Narnie Harshoe <[email protected]>
'''
from decimal import Decimal as dec
from decimal import ROUND_HALF_UP
from decimal import ROUND_CEILING
from decimal import ROUND_DOWN
from decimal import ROUND_FLOOR
from decimal import ROUND_HALF_DOWN
from decimal import ROUND_HALF_EVEN
from decimal import ROUND_UP
from decimal import ROUND_05UP
if type(number) == type(float()):
number = str(number)
if rounding == None:
rounding = ROUND_HALF_UP
place = '1.'
for i in range(places):
place = ''.join([place, '0'])
return dec(number).quantize(dec(place), rounding=rounding)
J'espère que cela t'aides,
Narnie
Comportement d'arrondi de Python 2 dans Python 3.
Ajout de 1 à la 15ème décimale . Précision jusqu'à 15 chiffres.
round2=lambda x,y=None: round(x+1e-15,y)
Python 3.x arrondit les valeurs .5 à un voisin qui est même
assert round(0.5) == 0
assert round(1.5) == 2
assert round(2.5) == 2
import decimal
assert decimal.Decimal('0.5').to_integral_value() == 0
assert decimal.Decimal('1.5').to_integral_value() == 2
assert decimal.Decimal('2.5').to_integral_value() == 2
toutefois, vous pouvez modifier l’arrondi décimal "retour" pour toujours arrondir .5, si nécessaire:
decimal.getcontext().rounding = decimal.ROUND_HALF_UP
assert decimal.Decimal('0.5').to_integral_value() == 1
assert decimal.Decimal('1.5').to_integral_value() == 2
assert decimal.Decimal('2.5').to_integral_value() == 3
i = int(decimal.Decimal('2.5').to_integral_value()) # to get an int
assert i == 3
assert type(i) is int
Certains cas:
in: Decimal(75.29 / 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
in: round(75.29 / 2, 2)
out: 37.65 GOOD
in: Decimal(85.55 / 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
in: round(85.55 / 2, 2)
out: 42.77 BAD
Pour le correctif:
in: round(75.29 / 2 + 0.00001, 2)
out: 37.65 GOOD
in: round(85.55 / 2 + 0.00001, 2)
out: 42.78 GOOD
Si vous voulez plus de décimales, par exemple 4, vous devriez ajouter (+ 0.0000001).
Travaille pour moi.
Exemple de reproduction:
['{} => {}'.format(x+0.5, round(x+0.5)) for x in range(10)]
['0.5 => 0', '1.5 => 2', '2.5 => 2', '3.5 => 4', '4.5 => 4', '5.5 => 6', '6.5 => 6', '7.5 => 8', '8.5 => 8', '9.5 => 10']
API: https://docs.python.org/3/library/functions.html#round
États:
Renvoie le nombre arrondi à la précision de ndigits après le point décimal. Si ndigits est omis ou est None, il renvoie l'entier le plus proche de son contribution.
Pour les types intégrés prenant en charge round (), les valeurs sont arrondies au multiple le plus proche de 10 de la puissance moins ndigits; si deux multiples sont également proches, l’arrondi est fait vers le choix pair (ainsi, par exemple, les arrondis (0.5) et arrondis (-0.5) valent 0 et arrondis (1.5) vaut 2) . Toute valeur entière est valide pour ndigits (positif, nul ou négatif) . La valeur de retour est un entier si ndigits est omis ou None . Sinon, la valeur de retour a le même type que nombre.
Pour un numéro d'objet Python général, arrondissez les délégués à nombre .round.
Note Le comportement de round () pour les floats peut être surprenant: pour Par exemple, round (2.675, 2) donne 2,67 au lieu des 2,68 attendus. Ce n’est pas un bug: c’est le résultat du fait que la plupart des fractions décimales ne peut pas être représenté exactement comme un float. Voir virgule flottante Arithmétique: problèmes et limitations pour plus d'informations.
Compte tenu de cette idée, vous pouvez utiliser quelques mathématiques pour le résoudre
import math
def my_round(i):
f = math.floor(i)
return f if i - f < 0.5 else f+1
maintenant, vous pouvez exécuter le même test avec my_round au lieu de round.
['{} => {}'.format(x + 0.5, my_round(x+0.5)) for x in range(10)]
['0.5 => 1', '1.5 => 2', '2.5 => 3', '3.5 => 4', '4.5 => 5', '5.5 => 6', '6.5 => 7', '7.5 => 8', '8.5 => 9', '9.5 => 10']
L'opérateur round arrondit la valeur à la valeur entière la plus proche.
Par exemple :
Si la valeur est supérieure à 0,5, elle sera arrondie à 1
print(round(211.5554, 2)) // output is 211.56
Si la valeur est inférieure à 0,5, elle sera arrondie à 0
print(round(211.5544, 2)) // output is 211.55
Modifier :
L'opérateur // mentionné précédemment n'est pas utilisé pour arrondir, il sert par exemple à gérer la sortie de la division.
print(10//3) // output is 3 instead of 3.3333333333333335