web-dev-qa-db-fra.com

Comment vérifier la précision décimale par défaut lors de la conversion de float en str?

Lors de la conversion d'une float en str, je peux spécifier le nombre de points décimaux que je souhaite afficher.

'%.6f' % 0.1
> '0.100000'
'%.6f' % .12345678901234567890
> '0.123457'

Mais lorsque vous appelez simplement str sur une float en python 2.7, il semble que le nombre par défaut soit au maximum de 12 décimales.

str(0.1)
>'0.1'
str(.12345678901234567890)
>'0.123456789012'

Où ce nombre maximum de points décimaux est-il défini/documenté? Puis-je obtenir ce numéro par programme?

21
C_Z_

Le nombre de décimales affichées va varier considérablement et il n’existera aucun moyen de prédire le nombre de décimales affichées en Python pur. Certaines bibliothèques telles que numpy vous permettent de définir la précision de la sortie.

Ceci est simplement dû aux limitations de la représentation float .

Les parties pertinentes du lien expliquent comment Python choisit d'afficher les flottants.

Python n’imprime qu’une approximation décimale de la valeur décimale vraie de l’approximation binaire stockée par la machine.

Python permet de gérer le nombre de chiffres en affichant plutôt une valeur arrondie

Maintenant, il y a la possibilité de chevauchement ici:

Fait intéressant, de nombreux nombres décimaux différents partagent la même fraction binaire approximative la plus proche.

La méthode de sélection des valeurs décimales à afficher a été modifiée dans Python 3.1 (mais la dernière phrase implique qu'il peut s'agir d'un détail d'implémentation).

Par exemple, les nombres 0.1 et 0.10000000000000001 sont tous deux approximée par 3602879701896397/2 ** 55. Depuis toutes ces décimales les valeurs partagent la même approximation, l'une d'elles pourrait être affiché tout en préservant l'invariant eval (repr (x)) == x

Historiquement, l’invite Python et la fonction intégrée repr () auraient pour choisissez celui avec 17 chiffres significatifs, 0.10000000000000001 . Depuis Python 3.1, Python (sur la plupart des systèmes) est maintenant capable de choisissez le plus court parmi ceux-ci et affichez simplement 0,1.

5
user3483203

Je ne crois pas que cela existe dans les spécifications du langage python. Cependant, l'implémentation de cpython le spécifie. La fonction float_repr() , qui transforme un float en chaîne, appelle finalement une fonction d'assistance avec le formateur 'r', qui appelle une fonction utilitaire qui code en dur le format jusqu'à format(float, '.16g'). Ce code peut être vu ici . Notez que ceci est pour python3.6.

>>> import math
>>> str(math.pi*4)
12.5663706144

donnant le nombre maximum de chiffres de signification (à la fois avant et après la virgule) à 16. Il semble que dans l'implémentation de python2.7, cette valeur ait été codée en dur à .12g. En ce qui concerne la raison pour laquelle cela s’est produit (et manque quelque peu de documentation, on peut trouver ici .)

Donc, si vous essayez d’obtenir combien de temps un nombre sera formaté lors de l’impression, obtenez simplement sa longueur avec .12g.

def len_when_displayed(n):
     return len(format(n, '.12g'))
3
modesitt

Eh bien, si vous recherchez un moyen purement python d’y parvenir, vous pouvez toujours utiliser quelque chose comme:

len(str(.12345678901234567890).split('.')[1])
>>>> 12

Je ne pouvais pas le trouver dans la documentation et l’ajouterais ici, mais c’est un moyen de contourner le problème qui peut au moins toujours renvoyer la longueur de précision si vous voulez savoir avant. 

Comme vous l'avez dit, il semble toujours qu'il s'agisse de 12 même lorsque vous alimentez des points flottants plus gros.


D'après ce que j'ai pu trouver, ce nombre peut être très variable et dans ces cas, le trouver empiriquement semble être le moyen le plus fiable de le faire. Donc, je définirais une méthode simple comme celle-ci,

def max_floating_point():
    counter = 0
    current_length = 0
    str_rep = '.1'
    while(counter <= current_length):
        str_rep += '1'
        current_length = len(str(float(str_rep)).split('.')[1])
        counter += 1

    return current_length

Cela vous retournera la représentation de longueur maximale sur votre système actuel,

print max_floating_point()
>>>> 12
3
scharette

En regardant la sortie des nombres aléatoires convertis, je suis incapable de comprendre comment la longueur de str() est déterminée, par exemple. sous Python 3.6.6:

>>> str(.123456789123456789123456789)
'0.12345678912345678'
>>> str(.111111111111111111111111111)
'0.1111111111111111'

Vous pouvez opter pour ce code qui simule réellement votre situation réelle:

import random
maxdec=max(map(lambda x:len(str(x)),filter(lambda x:x>.1,[random.random() for i in range(99)])))-2

Nous testons ici la longueur de ~ 90 nombres aléatoires dans l'intervalle ouvert (.1,1) après conversion (en déduisant le 0. de la gauche, d'où le -2) . Python 2.7.5 sur un Linux 64 bits me donne 12, et Python 3.4.8 et 3.6.6 m'en donnent 17.

0
tif