Ваша проблема недостаточно указана, вам нужно отступить назад и задать несколько вопросов.
- Какой тип (ы) ваши входные данные?
- Какой тип (типы) делают Вы хотите для своих результатов?
- Для результатов меньше 1, что именно вы хотите округлить? Вы хотите фактические степени 10 или приближения с плавающей точкой степеней 10? Вы знаете, что отрицательные степени 10 не могут быть выражены точно с плавающей точкой, верно? Предположим на данный момент, что вы хотите приближения с плавающей запятой степеней 10.
- Если входное значение равно степени 10 (или ближайшее приближение с плавающей запятой степени 10), если выходное значение будет таким же как вход? Или это должна быть следующая степень 10? «10 -> 10» или «10 -> 100»? Давайте пока предположим первое.
- Могут ли ваши входные значения быть любыми возможными значениями рассматриваемых типов? или они более ограничены.
В другом ответе было предложено взять логарифм, затем округлить (функция потолка), затем возвести в степень.
def nextpow10(n):
return 10 ** math.ceil(math.log10(n))
К сожалению, это страдает от ошибки округления. Прежде всего, n преобразуется из любого типа данных, в который он входит, в число с плавающей запятой двойной точности, что потенциально может привести к ошибкам округления, а затем вычисляется логарифм, потенциально вносящий больше ошибок округления как во внутренние вычисления, так и в его результат.
Таким образом, мне не понадобилось много времени, чтобы найти пример, в котором он дал неверный результат.
>>> 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
Теоретически возможно, что он потерпит неудачу в другом направлении, хотя, похоже, это так. провоцировать гораздо труднее.
Так что для надежного решения для чисел с плавающей запятой и целочисленных значений нам нужно предположить, что значение нашего логарифма является лишь приблизительным, и поэтому мы должны протестировать пару возможностей. Что-то вроде
def nextpow10(n):
p = round(math.log10(n))
r = 10 ** p
if r < n:
r = 10 ** (p+1)
return r;
Я считаю, что этот код должен давать правильные результаты для всех аргументов в разумном диапазоне величин реального мира. Он сломается для очень малого или очень большого числа нецелочисленных и не плавающих типов из-за проблем, конвертирующих их в плавающую Python особые случаи целочисленные аргументы для функции log10 в попытке предотвратить переполнение, но все же с достаточно большим целым числом может быть возможно вызвать неправильные результаты из-за ошибок округления.
Для тестирования двух реализаций I использовала следующую тестовую программу.
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
Это находит множество сбоев в простой реализации, но ни одной в улучшенной реализации.