Пітон круглий до наступної найвищої потужності 10


44

Як мені вдалося б виконати math.ceilтак, щоб число було присвоєне наступній найвищій потужності 10?

# 0.04  ->  0.1
# 0.7   ->  1
# 1.1   ->  10  
# 90    ->  100  
# ...

Моє поточне рішення - це словник, який перевіряє діапазон вхідного числа, але він є жорстким кодом, і я вважаю за краще однолінійне рішення. Можливо, тут я пропускаю простий математичний трюк або відповідну функцію нумету?


3
@bold виглядає як ці рішення працюють від 10до, це потрібно що - то з напр log10.
jonrsharpe

3
Слово, яке ви хочете, - "влада". Можливо, ви отримали неправильний переклад слова для рідної мови.
user2357112 підтримує Моніку

Спасибі, Моніка! @bold: Я знайшов це питання, але це інша проблема. Jonrsharpe дав ідеальну відповідь
offeltoffel

2
Це також пов’язано з порядком величини . 1 - 0-й, 10 - 1-й, 100 - 2-й, тощо
wjandrea

Відповіді:


60

Ви можете використовувати math.ceilз , math.log10щоб зробити це:

>>> 10 ** math.ceil(math.log10(0.04))
0.1
>>> 10 ** math.ceil(math.log10(0.7))
1
>>> 10 ** math.ceil(math.log10(1.1))
10
>>> 10 ** math.ceil(math.log10(90))
100

log10(n)дає вам рішення, xяке задовольняє 10 ** x == n, тож якщо ви округлите, xце дає вам показник наступної найвищої потужності 10.

Зауважте, що для значення, nде xвже є ціле число, "наступна найвища потужність 10" буде n:

>>> 10 ** math.ceil(math.log10(0.1))
0.1
>>> 10 ** math.ceil(math.log10(1))
1
>>> 10 ** math.ceil(math.log10(10))
10

1
Робота з функцією журналу, здається, лише хитрість, яку я не міг придумати. Я вірю, що саме на це я сподівався! Велике спасибі
offeltoffel

2
Примітка: залежно від бажаної поведінки, це не працює для повноважень 10, наприклад 10 ** math.ceil(math.log10(1)) == 1, яка не є "наступною найвищою силою"
Cireo

5
Примітка: ця відповідь покладається на арифметику з плаваючою комою, і як така вона може вийти з ладу через помилки округлення. Спробуйте годувати, наприклад, 1000000000000001.
підключення

2
@plugwash не обов'язково, математичні функції також прийматимуть, наприклад, decimal.Decimals.
jonrsharpe

5
Так, ви можете передавати інші типи, але вони будуть перетворені на подвійну точність числа з плаваючою точкою та передані функції C "log10". Існує особливий випадок, щоб запобігти переповненню журналів великої кількості, але нічого не запобігає помилкам округлення.
підключення

21

Ваша проблема недостатньо вказана, вам потрібно відступити і задати деякі питання.

  • Які види (типи) є вашими вхідними даними?
  • Якого типу (-ів) ви хочете для своїх результатів?
  • Для результатів менше 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

Теоретично також можливо, що він провалиться в іншому напрямку, хоча це, здається, набагато важче спровокувати.

Тож для надійного рішення для плавців та ints нам потрібно припустити, що значення нашого логарифму лише приблизне, і тому ми повинні перевірити пару можливостей. Щось по лінії

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

Це знаходить багато невдач у наївній реалізації, але жодної в покращеній реалізації.


Дякуємо за ваші зусилля, щоб тут детальніше розібратися. Хоча відповідь jonrsharpe вже вирішила мою проблему, ця відповідь може бути корисною для інших, хто має подібні, але більш конкретні питання.
offeltoffel

1
Для чого ви використовуєте roundзамість math.ceil? Це введе багато непотрібних випадків, коли r < nце правда, і тому їй потрібно виконати додаткову роботу.
a_guest

1
Тому що журнал може бути відключений у будь-якому напрямку.
підключення

1
використовуючи «покращений» код , але з круглою замінений результатами Math.ceil в аваріях для 1e-317 на нижньому кінці і 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 на високому кінці.
підключіть

1
(на практиці це, мабуть, добре)
підключіть

3

Здається, ви хочете скоріше найнижчу наступну потужність 10 ... Ось спосіб, використовуючи чисту математику і не журнал, а рекурсію.

def ceiling10(x):
    if (x > 10):
        return ceiling10(x / 10) * 10
    else:
        if (x <= 1):
            return ceiling10(10 * x) / 10
        else:
            return 10
for x in [1 / 1235, 0.5, 1, 3, 10, 125, 12345]:
    print(x, ceiling10(x))

Щойно перевірений цей, я даю йому підсумок, оскільки, здається, він працює в більшості практичних випадків, але він, мабуть, страждає від помилок округлення з досить невеликими введеннями. Стеля10 (1е-6) дає 1.0000000000000002е-06
підключення

0
y = math.ceil(x)
z = y + (10 - (y % 10))

Щось подібне, можливо? Це просто вгорі голови, але це спрацювало, коли я спробував кілька номерів у терміналі.


0

Заціни!

>>> i = 0.04123; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )               
0.04123 0.1
>>> i = 0.712; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )                 
0.712 1
>>> i = 1.1; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )                   
1.1 10
>>> i = 90; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )                    
90 100

Цей код заснований на принципі десяти потужності в Росії len( str( int( float_number ) ) ) .

Є 4 випадки:

    1. int( i ) > 1.

    Floatчисло, перетворене на intнаступний рядок str()з нього, дасть нам, stringз lengthяким саме ми шукаємо. Отже, перша частина для введення i > 1.0- це десять 10потужностей такої довжини.

    1. & 3. Маленьке розгалуження: i > 1.0і i > 0.1<=> це 10і 1відповідно.
    1. І останній випадок, коли i < 0.1: Ось десять мають негативну силу. Щоб отримати перший ненульовий елемент після коми, я використав таку побудову ("%.100f" % i ).replace('.','').index( k ), де k працює за [1:10]інтервал. Після цього візьміть мінімум списку результатів. І зменшити на одиницю, це спочатку нуль, який слід підрахувати. Крім того , тут стандарт мови Python index()може відбутися збій, якщо він не буде знайти принаймні , один з ненульових елементів з [1:10]інтервалу, тому в кінці кінців я повинен «фільтр» список входження: if str( j ) in "%.100f" % i. Крім того, щоб отримати більш глибоку точність - це %.100fможе бути різним.

Додайте будь-яке пояснення, будь ласка.
Мобін Рандбар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.