Frage

Ich mag eine Gleitkommazahl repräsentieren als eine Zeichenfolge zu einem gewissen Anzahl von signifikanten Stellen gerundet, und nie das exponentielle Format. Im Wesentlichen will, muss ich jede Gleitkommazahl anzuzeigen und stellen Sie sicher, dass es „sieht nett“.

Es gibt mehrere Teile für dieses Problem:

  • Ich muss in der Lage sein, die angeben, Anzahl der signifikanten Stellen.
  • Die Anzahl der signifikanten Stellen muss variabel sein, was nicht sein kann getan mit der Zeichenfolge Formatierung Betreiber . [Bearbeiten] Ich habe korrigiert worden; der Zeichenfolge Formatierung Bediener kann dies tun.
  • Ich brauche es die Art und Weise einer gerundet werden Person erwarten würde, nicht etwas, wie 1,999999999999

Ich habe einen Weg, dies zu tun, herausgefunden, obwohl es wie ein Werk der ganzen aussieht und es ist nicht ganz perfekt. (Die maximale Genauigkeit beträgt 15 signifikante Stellen.)

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

Gibt es einen besseren Weg, dies zu tun? Warum nicht Python haben eine eingebaute Funktion für das?

War es hilfreich?

Lösung

Es scheint, gibt es keine eingebaut ist Zeichenfolge Trick Formatierung, die Sie (1) Druck Schwimmern, deren erste signifikante Stelle erscheint nach dem Ort der 15. Dezimalstelle erlaubt und (2) nicht in wissenschaftlicher Notation. So dass Blätter manuelle String-Manipulation.

Im Folgenden werde ich die decimal Modul verwenden, um die Dezimalstellen von dem Schwimmer zu extrahieren. Die float_to_decimal Funktion wird verwendet, um den Schwimmer zu einem Decimal Objekt zu konvertieren. Der offensichtliche Weg decimal.Decimal(str(f)) ist falsch, weil str(f) signifikante Stellen verlieren kann.

float_to_decimal wurde aus der dezimal Moduls Dokumentation .

Sobald die Dezimalstellen als Tupel von Ints erhalten werden, unter dem Code tut die offensichtliche Sache: abhacken der gewünschten Anzahl von sigificant Ziffern, rund, wenn nötig, kommen Sie mit den Ziffern zusammen in einen String, tack auf einem Schild, stellt einen Dezimalpunkt und Nullen nach links oder rechts, wie angemessen.

Am unteren Rand werden Sie einige Fälle finde ich verwendet, um die f Funktion zu testen.

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)

Andere Tipps

Wenn Sie punktgenau wollen schwimmend müssen Sie das decimal Modul verwenden, die einen Teil der Python ist Standard Library :

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

Hier ist ein Ausschnitt, dass Formate ein Wert an die gegebenen Fehlerbalken nach.

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

Beispiele:

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

beliebige Genauigkeit Schwimmer nötig ist, um diese Frage richtig zu beantworten. Deshalb ist die Verwendung von dezimal Modul ist ein Muss. Es gibt keine Methode ist eine Dezimalzahl in einen String, ohne jemals mit dem exponentiellen Format (ein Teil der ursprünglichen Frage) zu konvertieren, so dass ich eine Funktion schrieb nur das zu tun:

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)

Das Problem zu Runde zu signifikanten Zahlen versucht. Dezimal die „Quantisierung ()“ Methode wird nicht rund höher als der Dezimalpunkt und die „round ()“ Funktion gibt immer einen Schwimmer. Ich weiß nicht, ob diese Fehler sind, aber es bedeutet, dass der einzige Weg, um rund unendliche Genauigkeit Gleitkommazahlen ist es als Liste oder Zeichenfolge zu analysieren und die Rundung manuell zu tun. Mit anderen Worten, es gibt keine vernünftige Antwort auf diese Frage.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top