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.

1178
demandé sur ShadowRanger 2009-01-18 21:16:41

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 .

1201
répondu Rex Logan 2018-05-25 22:36:13

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
444
répondu Xolve 2018-05-25 22:41:35

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 .

165
répondu chribsen 2018-05-25 22:55:26

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.

124
répondu grant zukowski 2015-08-28 08:33:19

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.

87
répondu 2009-01-18 18:40:03

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
66
répondu ax003d 2018-05-25 22:42:08

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 .

référence: convertissez le nombre de virgule flottante à une certaine précision, puis copiez en chaîne de caractères

49
répondu Clayton 2018-05-25 22:45:06

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).

45
répondu Alexey Antonenko 2018-05-25 22:56:17

vous pouvez modifier le format de sortie:

>>> a = 13.95
>>> a
13.949999999999999
>>> print "%.2f" % a
13.95
40
répondu Greg Hewgill 2018-05-25 22:36:41

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 et json ; 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.

La question


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+.

29
répondu hynekcer 2018-05-25 22:54:53

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 exactement 1/10 , sommant dix les valeurs de 0.1 peuvent ne pas donner exactement 1.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 .

19
répondu nosklo 2018-05-25 22:40:46

En Python 2.7:

a = 13.949999999999999
output = float("%0.2f"%a)
print output
13
répondu Shashank Singh 2018-09-23 11:38:47

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')
12
répondu Matt Fletcher 2017-12-12 14:45:44

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.

11
répondu HUAGHAGUAH 2018-05-25 22:37:18

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

7
répondu toto_tico 2018-02-23 23:05:33

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')
6
répondu Siamand Maroufi 2018-05-25 22:49:19
orig_float = 232569 / 16000.0

14.5355625

short_float = float("{:.2f}".format(orig_float)) 

14.54

4
répondu MikeL 2017-09-04 12:38:06

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
1
répondu iblasi 2018-05-25 22:50:12

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!

-12
répondu tdh 2015-12-31 08:05:56