Il tuo problema è sotto-specificato, devi fare un passo indietro e porre alcune domande.
- Che tipo (i) sono i tuoi input?
- Che tipo / i vuoi per le tue uscite?
- Per risultati inferiori a 1, cosa vuoi esattamente arrotondare? Vuoi poteri effettivi di 10 o approssimazioni in virgola mobile di poteri di 10? Sei consapevole che i poteri negativi di 10 non possono essere espressi esattamente in virgola mobile, giusto? Supponiamo per ora che tu voglia approssimazioni in virgola mobile di potenze di 10.
- Se l'ingresso è esattamente una potenza di 10 (o l'approssimazione in virgola mobile più vicina di una potenza di 10), l'uscita dovrebbe essere uguale all'ingresso? O dovrebbe essere il prossimo potere di 10 in su? "10 -> 10" o "10 -> 100"? Supponiamo che il primo per ora.
- I tuoi valori di input possono essere qualsiasi valore possibile dei tipi in questione? o sono più vincolati.
In un'altra risposta è stato proposto di prendere il logaritmo, quindi arrotondare per eccesso (funzione soffitto), quindi esponenziare.
def nextpow10(n):
return 10 ** math.ceil(math.log10(n))
Purtroppo questo soffre di errori di arrotondamento. Prima di tutto n viene convertito da qualunque tipo di dati capiti in un numero in virgola mobile a doppia precisione, potenzialmente introducendo errori di arrotondamento, quindi il logaritmo viene calcolato potenzialmente introducendo più errori di arrotondamento sia nei suoi calcoli interni che nei suoi risultati.
In quanto tale, non mi ci è voluto molto per trovare un esempio in cui ha dato un risultato errato.
>>> import math
>>> from numpy import nextafter
>>> n = 1
>>> while (10 ** math.ceil(math.log10(nextafter(n,math.inf)))) > n:
... n *= 10
...
>>> n
10
>>> nextafter(n,math.inf)
10.000000000000002
>>> 10 ** math.ceil(math.log10(10.000000000000002))
10
È anche teoricamente possibile che fallisca nella direzione opposta, anche se questo sembra essere molto più difficile da provocare.
Quindi per una soluzione solida per float e ints dobbiamo supporre che il valore del nostro logaritmo sia solo approssimativo, e quindi dobbiamo testare un paio di possibilità. Qualcosa del genere
def nextpow10(n):
p = round(math.log10(n))
r = 10 ** p
if r < n:
r = 10 ** (p+1)
return r;
Credo che questo codice dovrebbe dare risultati corretti per tutti gli argomenti in una gamma ragionevole di dimensioni del mondo reale. Si romperà per un numero molto piccolo o molto grande di tipi non interi e non in virgola mobile a causa di problemi di conversione in virgola mobile. Argomenti interi di Python per la funzione log10 nel tentativo di impedire l'overflow, ma con un numero intero sufficientemente massiccio potrebbe essere possibile forzare risultati errati a causa di errori di arrotondamento.
Per testare le due implementazioni ho usato il seguente programma di test.
n = -323 # 10**-324 == 0
while n < 1000:
v = 10 ** n
if v != nextpow10(v): print(str(v)+" bad")
try:
v = min(nextafter(v,math.inf),v+1)
except:
v += 1
if v > nextpow10(v): print(str(v)+" bad")
n += 1
Questo trova molti errori nell'implementazione ingenua, ma nessuno nell'implementazione migliorata.
10
, questo avrà bisogno di qualcosa con eslog10
.