Domanda

voglio rappresentare un numero a virgola mobile come stringa arrotondato a qualche numero di cifre significative, e non utilizzando il formato esponenziale. In sostanza, voglio visualizzare qualsiasi numero in virgola mobile e assicurarsi che “sembra bello”.

Ci sono molte parti di questo problema:

Non ho capito un modo per farlo, anche se sembra un lavoro tutto e non è del tutto perfetto. (La precisione massima è di 15 cifre significative.)

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

C'è un modo migliore per fare questo? Perché Python non ha una funzione incorporata per questo?

È stato utile?

Soluzione

Sembra non c'è è built-in di formattazione trucco che permette di galleggianti (1) Stampa la cui prima significativa cifre appare dopo il punto decimale e 15 (2), non in notazione scientifica. In modo che le foglie manuale di manipolazione di stringhe.

In basso uso del modulo decimal per estrarre le cifre decimali dal galleggiante. La funzione float_to_decimal viene utilizzata per convertire il galleggiante a un oggetto Decimal. Il modo più ovvio decimal.Decimal(str(f)) è sbagliato perché str(f) può perdere cifre significative.

float_to_decimal è stato revocato dalla decimale del modulo di documentazione .

Una volta che le cifre decimali sono ottenuti come una tupla di interi, il codice qui sotto fa la cosa più ovvia: recidere il numero desiderato di cifre sigificant, fino turno, se necessario, si uniscono le cifre insieme in una stringa, virare su un cartello, posizionare un punto decimale e zeri a sinistra oa destra come appropriato.

In fondo troverete alcuni casi ho usato per testare la funzione 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)

Altri suggerimenti

Se si vuole punto di precisione floating è necessario utilizzare il modulo decimal, che fa parte del Python libreria standard :

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

Ecco un frammento che i formati di un valore in base ai dati barre di errore.

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

Esempi:

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

carri precisione arbitraria sono necessari per rispondere adeguatamente a questa domanda. Pertanto utilizzando il decimale modulo è un must. Non esiste un metodo per convertire un numero decimale in una stringa senza mai usare il formato esponenziale (parte della domanda originale), così ho scritto una funzione per fare proprio questo:

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)

Il problema sta cercando di turno a cifre significative. di decimali "quantizzazione ()" metodo verrà non rotonda superiore al punto decimale e la funzione "round ()" restituisce sempre un galleggiante. Non so se questi sono insetti, ma significa che l'unico modo per numeri in virgola mobile infinita precisione rotonde è quello di analizzare come un elenco o stringa e fare dell'arrotondamento manualmente. In altre parole, non c'è una risposta sensata a questa domanda.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top