Limitation des flotteurs à deux décimales
je veux que a
soit arrondi à 13.95 .
>>> a
13.949999999999999
>>> round(a, 2)
13.949999999999999
la fonction round
ne fonctionne pas comme je l'espérais.
19 réponses
vous rencontrez l'ancien problème avec les nombres à virgule flottante que tous les nombres ne peuvent pas être représentés. La ligne de commande ne fait que vous montrer le formulaire flottant complet de mémoire.
en virgule flottante votre version arrondie est le même nombre. Puisque les ordinateurs sont binaires, ils stockent des nombres à virgule flottante comme un entier et le divisent ensuite par une puissance de deux de sorte que 13.95 sera représenté de la même façon que 125650429603636838/(2**53).
double précision les nombres ont 53 bits (16 chiffres) de précision et les flotteurs réguliers ont 24 bits (8 chiffres) de précision. Le floating point en Python utilise la double précision pour stocker les valeurs.
par exemple,
>>> 125650429603636838/(2**53)
13.949999999999999
>>> 234042163/(2**24)
13.949999988079071
>>> a=13.946
>>> print(a)
13.946
>>> print("%.2f" % a)
13.95
>>> round(a,2)
13.949999999999999
>>> print("%.2f" % round(a,2))
13.95
>>> print("{0:.2f}".format(a))
13.95
>>> print("{0:.2f}".format(round(a,2)))
13.95
>>> print("{0:.15f}".format(round(a,2)))
13.949999999999999
si vous êtes après seulement deux décimales comme dans la devise, alors vous avez un couple de meilleurs choix: 1) utiliser des entiers et stocker des valeurs en cents, pas dollars et puis diviser par 100 pour convertir de dollars. 2) ou utilisez un nombre de point fixe comme décimal .
Il y a de nouvelles spécifications de format, Chaîne de Spécification de Format Mini-Langage :
Vous pouvez faire la même chose que:
"{0:.2f}".format(13.949999999999999)
Note que ce qui précède renvoie une chaîne. Pour obtenir comme flotteur, simplement envelopper avec float(...)
:
float("{0:.2f}".format(13.949999999999999))
Note que l'emballage avec float()
ne change rien:
>>> x = 13.949999999999999999
>>> x
13.95
>>> g = float("{0:.2f}".format(x))
>>> g
13.95
>>> x == g
True
>>> h = round(x, 2)
>>> h
13.95
>>> x == h
True
le round()
intégré fonctionne très bien en Python 2.7 ou plus tard.
exemple:
>>> round(14.22222223, 2)
14.22
Check out la documentation .
je pense que l'approche la plus simple est d'utiliser la fonction format()
.
par exemple:
a = 13.949999999999999
format(a, '.2f')
13.95
cela produit un nombre de flotteurs comme une chaîne arrondie à deux points décimaux.
la plupart des nombres ne peuvent pas être représentés exactement dans les flotteurs. Si vous voulez arrondir le nombre parce que c'est ce que votre formule mathématique ou un algorithme nécessite, alors vous voulez utiliser ronde. Si vous voulez simplement restreindre l'affichage à une certaine précision, alors n'utilisez même pas rond et formatez-le simplement comme cette chaîne. (Si vous voulez l'afficher avec une méthode alternative d'arrondi, et il y a des tonnes, alors vous devez mélanger les deux approches.)
>>> "%.2f" % 3.14159
'3.14'
>>> "%.2f" % 13.9499999
'13.95'
et enfin, bien que peut-être le plus important, si vous voulez exact math alors vous ne voulez pas de flotteurs du tout. L'exemple habituel est de traiter avec de l'argent et de stocker 'cents' comme un entier.
essayez le code ci-dessous:
>>> a = 0.99334
>>> a = int((a * 100) + 0.5) / 100.0 # Adding 0.5 rounds it up
>>> print a
0.99
avec Python < 3 (par exemple 2.6 ou 2.7), il y a deux façons de le faire.
# Option one
older_method_string = "%.9f" % numvar
# Option two (note ':' before the '.9f')
newer_method_string = "{:.9f}".format(numvar)
mais notez que pour les versions Python au-dessus de 3 (par exemple 3.2 ou 3.3), l'option deux est de préférence .
pour plus d'informations sur l'option deux, je suggère ce lien sur formatage de chaîne de caractères à partir de la documentation Python .
Et pour plus d'informations sur l'option un, ce lien suffira et dispose d'informations sur les différents drapeaux .
Utiliser
print"{:.2f}".format(a)
au lieu de
print"{0:.2f}".format(a)
parce que cette dernière peut conduire à des erreurs de sortie en essayant de produire des variables multiples (voir les commentaires).
vous pouvez modifier le format de sortie:
>>> a = 13.95
>>> a
13.949999999999999
>>> print "%.2f" % a
13.95
TLDR ;)
le problème d'arrondissement à l'entrée / sortie a été résolu par Python 2.7.0 et 3.1 définitivement.
Infini test:
import random
for x in iter(random.random, None): # Verify FOREVER that rounding is fixed :-)
assert float(repr(x)) == x # Reversible repr() conversion.
assert len(repr(round(x, 10))) <= 12 # Smart decimal places in repr() after round.
if x >= 0.1: # Implicit rounding to 12 significant digits
assert str(x) == repr(round(x, 12)) # by str() is good enough for small errors.
y = 1000 * x # Decimal type is excessive for shopping
assert str(y) == repr(round(y, 12 - 3)) # in the supermaket with Python 2.7+ :-)
Documentation
voir les notes de version Python 2.7-autres changements de langue le quatrième paragraphe:
Les Conversions entre les nombres à virgule flottante et les chaînes sont maintenant correctement arrondie sur la plupart des plates-formes. Ces conversions se produisent dans de nombreux endroits différents: str() sur les flotteurs et les nombres complexes; les constructeurs flottants et complexes; le formatage numérique; sérialiser et désisérialiser les flotteurs et les nombres complexes en utilisant les modules
marshal
,pickle
etjson
; l'analyse des flotteurs et des littérales imaginaires en code Python; et la conversion décimale à flotteur.lié à cela, le repr () d'un nombre de virgule flottante x renvoie maintenant un résultat basé sur la chaîne décimale la plus courte qui est garantie de faire un retour à x sous arrondissage correct (avec le mode arrondissage rond-demi-à-pair). Auparavant, il donnait une chaîne basée sur l'arrondissement x à 17 chiffres décimaux.
plus d'informations: : le formatage de float
avant Python 2.7 était similaire à l'actuel numpy.float64
. Les deux types utilisent la même double précision 64 bits IEEE 754 mantissa 52 bits. Une grande différence est que np.float64.__repr__
est formaté fréquemment avec un nombre décimal excessif de sorte qu'aucun bit ne peut être perdu, mais aucun nombre IEEE 754 valide existe entre 13.94999999999 et 13.9500000000001. Le résultat est pas nice et la conversion repr(float(number_as_string))
n'est pas réversible. D'autre part: float.__repr__
est formaté de sorte que chaque chiffre est important; la séquence est sans lacunes et la conversion est réversible. Tout simplement: si vous avez peut-être une pépite.float64 nombre, le convertir en flotteur normal afin d'être formaté pour les humains, pas pour les processeurs numériques, sinon rien de plus est nécessaire avec Python 2.7+.
le tutoriel Python a une annexe appelée arithmétique à virgule flottante: problèmes et limites . La lire. Il explique ce qui se passe et pourquoi Python fait de son mieux. Il y a même un exemple qui correspond au vôtre. Permettez-moi de citer un peu:
>>> 0.1 0.10000000000000001
vous pourriez être tenté d'utiliser le
round()
fonction de le couper à l'unique chiffres que vous attendez. Mais qui ne fait pas de différence:>>> round(0.1, 1) 0.10000000000000001
le problème est que Le binaire valeur à virgule flottante stockée pour
“0.1”
était déjà le meilleur possible binaire approximation à1/10
, donc essayer de autour d'elle de nouveau ne pouvez pas faire mieux: il était déjà aussi bon qu'il obtient.une autre conséquence est que depuis
0.1
n'est pas exactement1/10
, sommant dix les valeurs de0.1
peuvent ne pas donner exactement1.0
, soit:>>> sum = 0.0 >>> for i in range(10): ... sum += 0.1 ... >>> sum 0.99999999999999989
une alternative et solution à vos problèmes serait d'utiliser le module decimal
.
En Python 2.7:
a = 13.949999999999999
output = float("%0.2f"%a)
print output
personne ici ne semble l'avoir mentionné encore, alors permettez-moi de donner un exemple dans le format F-string/template-string de Python 3.6, qui je pense est magnifiquement soigné:
>>> f'{a:.2f}'
il fonctionne bien avec des exemples plus longs aussi, avec des opérateurs et n'ayant pas besoin parens:
>>> print(f'Completed in {time.time() - start:.2f}s')
Il fait exactement ce que vous lui avez dit de faire, et fonctionne correctement. En savoir plus sur floating point confusion et peut-être essayer décimal objets à la place.
comme @Matt l'a souligné, Python 3.6 fournit F-strings , et ils peuvent également utiliser paramètres imbriqués :
value = 2.34558
precision = 2
width = 4
print(f'result: {value:{width}.{precision}f}')
qui affichera result: 2.35
pour fixer le point flottant dans des langages de type dynamique tels que Python et JavaScript, j'utilise cette technique
# For example:
a = 70000
b = 0.14
c = a * b
print c # Prints 980.0000000002
# Try to fix
c = int(c * 10000)/100000
print c # Prints 980
vous pouvez également utiliser décimal comme suit:
from decimal import *
getcontext().prec = 6
Decimal(1) / Decimal(7)
# Results in 6 precision -> Decimal('0.142857')
getcontext().prec = 28
Decimal(1) / Decimal(7)
# Results in 28 precision -> Decimal('0.1428571428571428571428571429')
orig_float = 232569 / 16000.0
14.5355625
short_float = float("{:.2f}".format(orig_float))
14.54
pour arrondir un nombre À une résolution, la meilleure façon est la suivante, qui peut fonctionner avec n'importe quelle résolution (0,01 pour les deux décimales, ou même d'autres étapes):
>>> import numpy as np
>>> value = 13.949999999999999
>>> resolution = 0.01
>>> newValue = int(np.round(value/resolution))*resolution
>>> print newValue
13.95
>>> resolution = 0.5
>>> newValue = int(np.round(value/resolution))*resolution
>>> print newValue
14.0
la méthode que j'utilise est celle du tranchage de ficelles. C'est relativement simple et rapide.
tout d'abord, convertissez le flotteur en chaîne, choisissez la longueur que vous souhaitez qu'il soit.
float = str(float)[:5]
dans la ligne simple ci-dessus, nous avons converti la valeur en une chaîne, puis nous avons gardé la chaîne uniquement à ses quatre premiers chiffres ou caractères (inclus).
Espère que ça aide!