Twój problem jest nieokreślony, musisz cofnąć się i zadać kilka pytań.
- Jakie typy są twoje dane wejściowe?
- Jakiego rodzaju chcesz dla swoich wyników?
- W przypadku wyników mniejszych niż 1, co dokładnie chcesz zaokrąglić? Czy chcesz rzeczywistych mocy 10 lub przybliżonych liczb zmiennoprzecinkowych mocy 10? Czy zdajesz sobie sprawę, że mocy ujemnych 10 nie można dokładnie wyrazić w liczbach zmiennoprzecinkowych, prawda? Załóżmy na razie, że chcesz przybliżenia zmiennoprzecinkowe potęg 10.
- Jeśli wejście ma dokładnie moc 10 (lub najbliższe przybliżenie zmiennoprzecinkowe mocy 10), czy wynik powinien być taki sam jak na wejściu? A może powinna to być następna moc 10 w górę? „10 -> 10” czy „10 -> 100”? Załóżmy na razie to pierwsze.
- Czy twoje wartości wejściowe mogą być dowolną możliwą wartością danych typów? czy są bardziej ograniczeni.
W innej odpowiedzi zaproponowano, aby wziąć logarytm, a następnie zaokrąglić w górę (funkcja pułapu), a następnie potęgować.
def nextpow10(n):
return 10 ** math.ceil(math.log10(n))
Niestety cierpi to na błędy zaokrąglania. Przede wszystkim n jest konwertowane z dowolnego typu danych, który ma, na liczbę zmiennoprzecinkową o podwójnej precyzji, potencjalnie wprowadzając błędy zaokrąglania, następnie logarytm jest obliczany, potencjalnie wprowadzając więcej błędów zaokrąglania zarówno w wewnętrznych obliczeniach, jak i w wyniku.
Jako taki nie zajęło mi długo znalezienie przykładu, w którym dał niepoprawny wynik.
>>> 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
Teoretycznie możliwe jest, że zawiodą w innym kierunku, choć wydaje się to znacznie trudniejsze do sprowokowania.
Dlatego dla solidnego rozwiązania dla liczb zmiennoprzecinkowych i liczb wewnętrznych musimy założyć, że wartość naszego logarytmu jest jedynie przybliżona i dlatego musimy przetestować kilka możliwości. Coś w stylu
def nextpow10(n):
p = round(math.log10(n))
r = 10 ** p
if r < n:
r = 10 ** (p+1)
return r;
Uważam, że ten kod powinien dawać poprawne wyniki dla wszystkich argumentów w rozsądnym zakresie wielkości rzeczywistych. Zepsuje się dla bardzo małej lub bardzo dużej liczby typów niecałkowitych i nie zmiennoprzecinkowych z powodu problemów z konwersją ich na zmiennoprzecinkowe. Python w szczególnych przypadkach argumentuje liczby całkowite dla funkcji log10, próbując zapobiec przepełnieniu, ale wciąż przy wystarczająco dużej liczbie całkowitej może być możliwe wymuszenie niepoprawnych wyników z powodu błędów zaokrąglania.
Aby przetestować dwie implementacje, użyłem następującego programu testowego.
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
Stwierdzono wiele błędów we wdrożeniu naiwnym, ale nie we ulepszonym.
10
, będzie to wymagać czegoś nplog10
.