Question

Je veux représenter un nombre à virgule flottante comme une chaîne arrondie à un certain nombre de chiffres significatifs, et ne jamais utiliser le format exponentiel. Essentiellement, je veux afficher un nombre à virgule flottante et assurez-vous qu'il « l'air bien ».

Il y a plusieurs parties à ce problème:

  • Je dois être en mesure de préciser la nombre de chiffres significatifs.
  • Le nombre de chiffres significatifs doit être variable, ce qui ne peut pas être fait avec la mise en forme de chaîne opérateur. [Modifier] J'ai corrigé; l'opérateur formatage de chaîne peut le faire.
  • J'ai besoin pour arrondir la façon dont un personne peut s'y attendre, pas quelque chose comme 1,999999999999

J'ai compris une façon de le faire, mais il ressemble à un travail rond et il est pas tout à fait parfait. (La précision maximale est de 15 chiffres significatifs).

>>> def f(number, sigfig):
    return ("%.15f" % (round(number, int(-1 * floor(log10(number)) + (sigfig - 1))))).rstrip("0").rstrip(".")

>>> print f(0.1, 1)
0.1
>>> print f(0.0000000000368568, 2)
0.000000000037
>>> print f(756867, 3)
757000

Y at-il une meilleure façon de le faire? Pourquoi ne pas Python a une fonction intégrée pour cela?

Était-ce utile?

La solution

Il semble qu'il n'y ait pas de chaîne intégré formatage truc qui vous permet de (1) flotteurs d'impression dont la première apparaît chiffre significatif après la 15e décimale et (2) ne sont pas en notation scientifique. Alors que les feuilles manipulation de chaînes manuelle.

Ci-dessous j'utiliser le module decimal pour extraire les chiffres décimaux du flotteur. La fonction float_to_decimal est utilisé pour convertir le flotteur à un objet Decimal. La decimal.Decimal(str(f)) de façon évidente est erroné car str(f) peut perdre les chiffres significatifs.

float_to_decimal a été soulevé du décimal documentation du module de .

Une fois que les chiffres décimaux sont obtenus en tant que tuple de ints, le code fait en dessous de la chose évidente: côtelette hors le nombre désiré de chiffres sigificant, tour d'horizon si nécessaire, joindre les chiffres ensemble dans une chaîne, amure sur un signe, placer un point décimal et des zéros à gauche ou à droite, selon le cas.

En bas, vous trouverez quelques cas, j'utilisé pour tester la fonction f.

import decimal

def float_to_decimal(f):
    # http://docs.python.org/library/decimal.html#decimal-faq
    "Convert a floating point number to a Decimal with no loss of information"
    n, d = f.as_integer_ratio()
    numerator, denominator = decimal.Decimal(n), decimal.Decimal(d)
    ctx = decimal.Context(prec=60)
    result = ctx.divide(numerator, denominator)
    while ctx.flags[decimal.Inexact]:
        ctx.flags[decimal.Inexact] = False
        ctx.prec *= 2
        result = ctx.divide(numerator, denominator)
    return result 

def f(number, sigfig):
    # http://stackoverflow.com/questions/2663612/nicely-representing-a-floating-point-number-in-python/2663623#2663623
    assert(sigfig>0)
    try:
        d=decimal.Decimal(number)
    except TypeError:
        d=float_to_decimal(float(number))
    sign,digits,exponent=d.as_tuple()
    if len(digits) < sigfig:
        digits = list(digits)
        digits.extend([0] * (sigfig - len(digits)))    
    shift=d.adjusted()
    result=int(''.join(map(str,digits[:sigfig])))
    # Round the result
    if len(digits)>sigfig and digits[sigfig]>=5: result+=1
    result=list(str(result))
    # Rounding can change the length of result
    # If so, adjust shift
    shift+=len(result)-sigfig
    # reset len of result to sigfig
    result=result[:sigfig]
    if shift >= sigfig-1:
        # Tack more zeros on the end
        result+=['0']*(shift-sigfig+1)
    elif 0<=shift:
        # Place the decimal point in between digits
        result.insert(shift+1,'.')
    else:
        # Tack zeros on the front
        assert(shift<0)
        result=['0.']+['0']*(-shift-1)+result
    if sign:
        result.insert(0,'-')
    return ''.join(result)

if __name__=='__main__':
    tests=[
        (0.1, 1, '0.1'),
        (0.0000000000368568, 2,'0.000000000037'),           
        (0.00000000000000000000368568, 2,'0.0000000000000000000037'),
        (756867, 3, '757000'),
        (-756867, 3, '-757000'),
        (-756867, 1, '-800000'),
        (0.0999999999999,1,'0.1'),
        (0.00999999999999,1,'0.01'),
        (0.00999999999999,2,'0.010'),
        (0.0099,2,'0.0099'),         
        (1.999999999999,1,'2'),
        (1.999999999999,2,'2.0'),           
        (34500000000000000000000, 17, '34500000000000000000000'),
        ('34500000000000000000000', 17, '34500000000000000000000'),  
        (756867, 7, '756867.0'),
        ]

    for number,sigfig,answer in tests:
        try:
            result=f(number,sigfig)
            assert(result==answer)
            print(result)
        except AssertionError:
            print('Error',number,sigfig,result,answer)

Autres conseils

Si vous voulez flottante précision de point que vous devez utiliser le module decimal, qui fait partie du Python standard Library :

>>> import decimal
>>> d = decimal.Decimal('0.0000000000368568')
>>> print '%.15f' % d
0.000000000036857

Voici un extrait qui met en forme une valeur en fonction des barres d'erreur donnés.

from math import floor, log10, round

def sigfig3(v, errplus, errmin):
    i = int(floor(-log10(max(errplus,errmin)) + 2))
    if i > 0:
        fmt = "%%.%df" % (i)
        return "{%s}^{%s}_{%s}" % (fmt % v,fmt % errplus, fmt % errmin)
    else:
        return "{%d}^{%d}_{%d}" % (round(v, i),round(errplus, i), numpy.round(i))

Exemples:

5268685 (+1463262,-2401422) becomes 5300000 (+1500000,-2400000)
0.84312 +- 0.173124 becomes 0.84 +- 0.17

flotteurs de précision arbitraires sont nécessaires pour bien répondre à cette question. Par conséquent, en utilisant la décimal le module est un must. Il n'y a pas de méthode pour convertir un nombre décimal en une chaîne sans jamais utiliser le format exponentiel (partie de la question initiale), j'ai donc écrit une fonction pour faire exactement cela:

def removeExponent(decimal):
    digits = [str(n) for n in decimal.as_tuple().digits]
    length = len(digits)
    exponent = decimal.as_tuple().exponent
    if length <= -1 * exponent:
        zeros = -1 * exponent - length
        digits[0:0] = ["0."] + ["0"] * zeros
    elif 0 < -1 * exponent < length:
        digits.insert(exponent, ".")
    elif 0 <= exponent:
        digits.extend(["0"] * exponent)
    sign = []
    if decimal.as_tuple().sign == 1:
        sign = ["-"]
    print "".join(sign + digits)

Le problème tente de tour à des chiffres significatifs. méthode de « Quantification () » de décimales ne sera pas rond plus haut que le point décimal, et la fonction « round () » retourne toujours un flotteur. Je ne sais pas si ce sont des bugs, mais cela signifie que la seule façon de précision infinie autour de nombres à virgule flottante est d'analyser comme une liste ou une chaîne et faire l'arrondi manuellement. En d'autres termes, il n'y a pas de réponse raisonnable à cette question.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top