Su problema está subespecificado, debe retroceder y hacer algunas preguntas.
- ¿De qué tipo (s) son sus entradas?
- ¿Qué tipo (s) desea para sus salidas?
- Para resultados inferiores a 1, ¿a qué quiere redondear exactamente? ¿Desea potencias reales de 10 o aproximaciones de coma flotante de potencias de 10? Eres consciente de que las potencias negativas de 10 no se pueden expresar exactamente en coma flotante, ¿verdad? Supongamos por ahora que desea aproximaciones de punto flotante de potencias de 10.
- Si la entrada es exactamente una potencia de 10 (o la aproximación de punto flotante más cercana de una potencia de 10), ¿la salida debería ser la misma que la entrada? ¿O debería ser el próximo poder de 10 arriba? "10 -> 10" o "10 -> 100"? Asumamos lo primero por ahora.
- ¿Pueden sus valores de entrada ser cualquier valor posible de los tipos en cuestión? o están más restringidos.
En otra respuesta, se propuso tomar el logaritmo, luego redondear hacia arriba (función de techo), luego exponer.
def nextpow10(n):
return 10 ** math.ceil(math.log10(n))
Lamentablemente, esto sufre de errores de redondeo. En primer lugar, n se convierte de cualquier tipo de datos que tenga en un número de coma flotante de doble precisión, lo que potencialmente introduce errores de redondeo, luego el logaritmo se calcula y posiblemente introduce más errores de redondeo tanto en sus cálculos internos como en su resultado.
Como tal, no me llevó mucho tiempo encontrar un ejemplo en el que proporcionara un resultado incorrecto.
>>> 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
También es teóricamente posible que falle en la otra dirección, aunque esto parece ser mucho más difícil de provocar.
Entonces, para una solución robusta para flotadores e ints, debemos suponer que el valor de nuestro logaritmo es solo aproximado y, por lo tanto, debemos probar un par de posibilidades. Algo en la línea de
def nextpow10(n):
p = round(math.log10(n))
r = 10 ** p
if r < n:
r = 10 ** (p+1)
return r;
Creo que este código debería dar resultados correctos para todos los argumentos en un rango sensible de magnitudes del mundo real. Se romperá para números muy pequeños o muy grandes de tipos no enteros y de punto flotante debido a problemas al convertirlos en punto flotante. Los argumentos enteros de casos especiales de Python para la función log10 en un intento de evitar el desbordamiento, pero aún con un número entero suficientemente masivo, es posible forzar resultados incorrectos debido a errores de redondeo.
Para probar las dos implementaciones utilicé el siguiente programa de prueba.
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
Esto encuentra muchas fallas en la implementación ingenua, pero ninguna en la implementación mejorada.
10
arriba, esto necesitará algo con, por ejemplolog10
.