web-dev-qa-db-fra.com

Flotteur rond à x décimales?

Existe-t-il un moyen de contourner a python float to x décimales? Par exemple:

>>> x = roundfloat(66.66666666666, 4)
66.6667
>>>x = roundfloat(1.29578293, 6)
1.295783

J'ai trouvé des moyens de les couper/tronquer (66.666666666 -> 66.6666), mais pas rond (66.666666666 -> 66.6667).

59
tkbx

Utilisez la fonction intégrée round():

In [23]: round(66.66666666666,4)
Out[23]: 66.6667

In [24]: round(1.29578293,6)
Out[24]: 1.295783

aide sur round():

round (number [ ndigits]) -> nombre en virgule flottante

Arrondir un nombre à une précision donnée en chiffres décimaux (0 chiffre par défaut). Cela retourne toujours un nombre à virgule flottante. La précision peut être négative.

65
Ashwini Chaudhary

Je me sens obligé de fournir un contrepoint à la réponse d'Ashwini Chaudhary. Malgré les apparences, la forme à deux arguments de la fonction round ne le fait pas autour de a Python flotte à un nombre donné de décimales, et ce n'est souvent pas la solution que vous voulez, même quand vous pensez que c'est le cas. Laissez-moi vous expliquer ...

La capacité à arrondir un flottant (Python) à un certain nombre de décimales est une demande fréquente, mais s'avère rarement être ce qui est réellement nécessaire. La réponse simple et séduisante round(x, number_of_places) est une nuisance attrayante: elle regarde comme si elle faisait ce que vous voulez, mais grâce au fait que Python Les floats sont stockés en interne en binaire, cela donne un résultat plus subtil. Prenons l'exemple suivant:

>>> round(52.15, 1)
52.1

Avec une compréhension naïve de ce que round fait, cela semble faux: il devrait sûrement être arrondi en haut à 52.2 plutôt que bas à 52.1? Pour comprendre pourquoi on ne peut pas compter sur de tels comportements, vous devez comprendre que si cela ressemble à une simple opération décimale à décimale, il est loin d'être simple.

Alors, voici ce qui se passe vraiment dans l'exemple ci-dessus. ( souffle profond) Nous affichons une décimale représentation du plus proche binaire nombre à virgule flottante au plus proche n- digits-après-le-point décimal nombre à un binaire approximation à virgule flottante d'un littéral numérique écrit en décimal. Donc, pour passer du littéral numérique original à la sortie affichée, la machine sous-jacente a effectué quatre conversions séparées entre formats binaires et décimaux, deux dans chaque direction. Décomposant (et avec les clauses de non-responsabilité habituelles concernant la prise en compte du format binaire64 IEEE 754, des arrondis égaux, et des règles IEEE 754):

  1. D'abord le littéral numérique 52.15 est analysé et converti en un Python float. Le nombre réel stocké est 7339460017730355 * 2**-47, ou 52.14999999999999857891452847979962825775146484375.

  2. En interne, en tant que première étape de l'opération round, Python) calcule la chaîne décimale à 1 chiffre après le point la plus proche du nombre stocké. touchez sous la valeur initiale de 52.15, on finit par arrondir et obtenir une chaîne 52.1. Ceci explique pourquoi nous obtenons 52.1 comme sortie finale au lieu de 52.2.

  3. Ensuite, dans la deuxième étape de l'opération round, Python transforme cette chaîne en virgule flottante, en obtenant le nombre à virgule flottante binaire le plus proche à 52.1, qui est maintenant 7332423143312589 * 2**-47, ou 52.10000000000000142108547152020037174224853515625.

  4. Enfin, dans le cadre de la boucle read-eval-print (REPL) de Python, la valeur en virgule flottante est affichée (en décimal). Cela implique de reconvertir la valeur binaire en chaîne décimale, obtenant ainsi 52.1 comme résultat final.

Dans Python 2.7 et versions ultérieures, nous nous trouvons dans une situation agréable: les deux conversions des étapes 3 et 4 s'annulent l'une l'autre. Cela est dû au choix de Python de repr implémentation, qui produit la valeur décimale la plus courte garantie pour arrondir correctement au flottant réel.L'une des conséquences de ce choix est que si vous commencez par un littéral décimal (ni trop grand, ni trop petit) avec 15 chiffres significatifs ou moins, le flottant correspondant s'affichera. ces mêmes chiffres exacts:

>>> x = 15.34509809234
>>> x
15.34509809234

Malheureusement, cela donne l’illusion que Python stocke les valeurs en décimal. Ce n’est pas le cas de Python 2.6, bien que!! Voici l’exemple original exécuté en Python 2.6:

>>> round(52.15, 1)
52.200000000000003

Non seulement nous arrondissons dans le sens opposé pour obtenir 52.2 au lieu de 52.1, mais la valeur affichée n’imprime même pas comme 52.2! Ce comportement a causé de nombreux rapports au traqueur de bogues Python) le long de "round is broken!". Mais ce n'est pas round qui est cassé, ce sont les attentes de l'utilisateur. (D'accord, ok, round est un petit bit cassé dans Python 2.6, en ce sens qu'il n'utilise pas l'arrondi correct.)

Version courte: si vous utilisez un tour à deux arguments et que vous vous attendez à un comportement prévisible d'une approximation binaire à un décimal d'un binary approximation d'un décimal à mi-chemin, vous demandez des ennuis.

Donc assez avec le "tour de deux arguments est mauvais" argument. Que devrait utilisez-vous à la place? Il y a quelques possibilités, selon ce que vous essayez de faire.

  • Si vous arrondissez à des fins d’affichage, vous ne voulez pas du tout un résultat flottant; vous voulez une ficelle. Dans ce cas, la réponse consiste à utiliser le formatage de chaîne:

    >>> format(66.66666666666, '.4f')
    '66.6667'
    >>> format(1.29578293, '.6f')
    '1.295783'
    

    Même dans ce cas, il faut connaître la représentation binaire interne pour ne pas être surpris par le comportement des cas à mi-chemin décimaux apparents.

    >>> format(52.15, '.1f')
    '52.1'
    
  • Si vous opérez dans un contexte où la direction décimale à mi-chemin des majuscules est arrondie (par exemple, dans certains contextes financiers), vous pouvez représenter vos nombres à l'aide du type Decimal. Faire un arrondi décimal sur le type Decimal a plus de sens que sur un type binaire (de même, arrondir à un nombre déterminé d'emplacements binaires est parfaitement logique sur un type binaire). De plus, le module decimal vous permet de mieux contrôler le mode d'arrondi. Dans Python 3, round effectue le travail directement. Dans Python 2, vous avez besoin de la méthode quantize.

    >>> Decimal('66.66666666666').quantize(Decimal('1e-4'))
    Decimal('66.6667')
    >>> Decimal('1.29578293').quantize(Decimal('1e-6'))
    Decimal('1.295783')
    
  • Dans de rares cas, la version à deux arguments de round vraiment est ce que vous voulez: vous êtes peut-être en train de biner dans des bacs de taille 0.01, et vous ne vous souciez pas vraiment de la direction que prennent les affaires frontalières. Cependant, ces cas sont rares et il est difficile de justifier l'existence de la version à deux arguments de la commande intégrée round basée uniquement sur ces cas.

112
Mark Dickinson