Per rispondere direttamente alla domanda, ecco la mia versione usando i nomi dalla funzione R :
import math
def signif(x, digits=6):
if x == 0 or not math.isfinite(x):
return x
digits -= math.ceil(math.log10(abs(x)))
return round(x, digits)
La mia ragione principale per pubblicare questa risposta sono i commenti che lamentano che "0,075" arriva a 0,07 anziché a 0,08. Ciò è dovuto, come sottolineato da "Novizio C", a una combinazione di aritmetica in virgola mobile avente sia una precisione finita che una rappresentazione base-2 . Il numero più vicino a 0,075 che può essere effettivamente rappresentato è leggermente più piccolo, quindi l'arrotondamento esce in modo diverso da quello che potresti aspettarti ingenuamente.
Si noti inoltre che ciò si applica a qualsiasi uso dell'aritmetica in virgola mobile non decimale, ad esempio C e Java hanno entrambi lo stesso problema.
Per mostrare più in dettaglio, chiediamo a Python di formattare il numero in formato "esadecimale":
0.075.hex()
che ci dà: 0x1.3333333333333p-4
. La ragione di ciò è che la normale rappresentazione decimale spesso comporta l'arrotondamento e quindi non è il modo in cui il computer "vede" effettivamente il numero. Se non sei abituato a questo formato, un paio di riferimenti utili sono i documenti Python e lo standard C. .
Per mostrare come funzionano questi numeri, possiamo tornare al nostro punto di partenza facendo:
0x13333333333333 / 16**13 * 2**-4
che dovrebbe stampare 0.075
. 16**13
è perché ci sono 13 cifre esadecimali dopo il punto decimale e2**-4
è perché gli esponenti esadecimali sono base-2.
Ora abbiamo un'idea di come sono rappresentati i float, possiamo usare il decimal
modulo per darci un po 'più di precisione, mostrandoci cosa sta succedendo:
from decimal import Decimal
Decimal(0x13333333333333) / 16**13 / 2**4
dare: 0.07499999999999999722444243844
e si spera spiegando perché round(0.075, 2)
valuta0.07