Ваша проблема недостаточно указана, вам нужно отступить и задать несколько вопросов.
- Какой тип (ы) ваши входы?
- Какой тип (ы) вы хотите для ваших результатов?
- Для результатов менее 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 в попытке предотвратить переполнение, но все же с достаточно большим целым числом может быть возможно вызвать неверные результаты из-за ошибок округления.
Для тестирования двух реализаций я использовал следующую тестовую программу.
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
Это находит много сбоев в наивной реализации, но не в улучшенной реализации.
10
сверху, для этого нужно что-то, напримерlog10
.