Python округлює ціле число до наступної сотні


78

Здається, про це вже слід було запитувати сотні разів (каламбур - це весело =), але я можу знайти лише функцію округлення поплавців. Як округлити ціле число, наприклад 130 -> 200:?


3
Ви хочете, щоб 100 також округлили до 200?
DSM

Ні, відповідь Томаса робить саме те, що мені потрібно
userBG

1
Відповідь Томаса робить від 100 до 200. Ось чому я запитав.
DSM

Перевірте редагування, я не звернув на це уваги в першій відповіді.
Thomas Orozco

1
@ofko: Ви прийняли відповідь, яка не відповідає великим цілим числам; див. мою оновлену відповідь для деталей.
John Machin

Відповіді:


150

Округлення зазвичай виконується на числах із плаваючою комою, і тут є три основні функції, які ви повинні знати: round(округляється до найближчого цілого числа), math.floor(завжди округлюється вниз) і math.ceil(завжди округлюється).

Ви запитуєте про цілі числа та округлення до сотень, але ми все одно можемо використовувати math.ceil, поки ваші числа менше 2 53 . Для використання math.ceilми просто ділимо на 100 спочатку, округляємо і помножуємо на 100 потім:

>>> import math
>>> def roundup(x):
...     return int(math.ceil(x / 100.0)) * 100
... 
>>> roundup(100)
100
>>> roundup(101)
200

Поділивши на 100 спочатку і помноживши на 100 згодом, "зміщує" два знаки після коми вправо та вліво, так що math.ceilпрацює на сотні. Ви можете використовувати 10**nзамість 100, якщо хочете округлити до десятків ( n = 1), тисяч ( n = 3) тощо.

Альтернативний спосіб зробити це - уникати чисел з плаваючою комою (вони мають обмежену точність) і замість цього використовувати лише цілі числа. Цілі числа мають довільну точність у Python, тому це дозволяє округляти числа будь-якого розміру. Правило округлення просте: знайдіть залишок після ділення зі 100 і додайте 100 мінус цей залишок, якщо він ненульовий:

>>> def roundup(x):
...     return x if x % 100 == 0 else x + 100 - x % 100

Це працює для номерів будь-якого розміру:

>>> roundup(100)
100
>>> roundup(130)
200
>>> roundup(1234567891234567891)
1234567891234567900L

Я зробив міні-орієнтир двох рішень:

$ python -m timeit -s 'import math' -s 'x = 130' 'int(math.ceil(x/100.0)) * 100'
1000000 loops, best of 3: 0.364 usec per loop
$ python -m timeit -s 'x = 130' 'x if x % 100 == 0 else x + 100 - x % 100'
10000000 loops, best of 3: 0.162 usec per loop

Чисте ціле рішення швидше в два рази порівняно з math.ceilрішенням.

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

$ python -m timeit -s 'x = 130' 'x + 100*(x%100>0) - x%100'
10000000 loops, best of 3: 0.167 usec per loop

Як останнє зауваження, дозвольте мені також зазначити, що якщо ви хотіли округлити 101–149 до 100 та 150–199 до 200, наприклад, округлити до найближчої сотні, то вбудована roundфункція може зробити це за вас:

>>> int(round(130, -2))
100
>>> int(round(170, -2))
200

Я не роблю тут звичайного округлення, якби я був так, я б використовував round ()
userBG

3
@ofko: так, ти хочеш округлити. math.ceilКанонічний спосіб зробити це - ділення і множення на 100 канонічний спосіб зробити round, ceilі floorпрацювати на сотні.
Martin Geisler

1
Після нещодавніх редагувань тепер має сенс прийняти цю відповідь.
userBG

2
-1 Цей підхід може бути "канонічним" з плаваючими кодами, але він ПОМИЛЯЄ з великими цілими числами. Детальніше див. У моїй відповіді. OP спеціально запитував цілі числа і не виражав верхньої межі розміру чисел.
John Machin

1
@JohnMachin: Голос проти - за питання, які "не є корисними", і я не розумію, чому ця проста та пряма відповідь не є корисною. Infact, ОП позначив його як прийнято, так що це буде корисно. Далі, коли комусь потрібна допомога, щоб округлити 130 до 200, тоді я думаю, це трохи розтягує скаржитися на неправильне округлення 1234567891234567891. Ви маєте рацію, що точність обмежена floatпорівняно з long(звичайно!), Але для більшості практичних ситуацій a floatє досить великою.
Мартін Гейслер

26

Це пізня відповідь, але існує просте рішення, яке поєднує в собі найкращі аспекти існуючих відповідей: наступне кратне 100вгору від xє x - x % -100(або, якщо вам більше подобається x + (-x) % 100).

>>> x = 130
>>> x -= x % -100  # Round x up to next multiple of 100.
>>> x
200

Це швидко і просто, дає правильні результати для будь-яких цілих чисел x(наприклад, відповідь Джона Махіна), а також дає обґрунтовані результати (за модулем звичайні застереження щодо подання з плаваючою точкою), якщо xє плаваючою точкою (як відповідь Мартіна Гейслера).

>>> x = 0.1
>>> x -= x % -100
>>> x
100.0

1
ваше рішення таке ж швидке, як і Мартіна, але позначення коротше. Дякую. % timeit 'x = 110' 'x - = x% -100' # 100000000 петлі, найкраще 3: 9,37 нс на петлю VS% timeit 'x = 110' 'x + 100 * (x% 100> 0) - x % 100 '# 100000000 петель, найкраще 3: 9,38 нс на цикл
тагома


18

Ось загальний спосіб округлення до найближчого кратного будь-якого додатного цілого числа:

def roundUpToMultiple(number, multiple):
    num = number + (multiple - 1)
    return num - (num % multiple)

Зразок використання:

>>> roundUpToMultiple (101, 100)
200
>>> roundUpToMultiple (654, ​​321)
963

еквівалент, коротший метод:lambda number, multiple: multiple * (1 + (number - 1) // multiple)
mic_e

8

Для aневід’ємних, bдодатних, обох цілих чисел:

>>> rup = lambda a, b: (a + b - 1) // b * b
>>> [(x, rup(x, 100)) for x in (199, 200, 201)]
[(199, 200), (200, 200), (201, 300)]

Оновлення Прийнята в даний час відповідь розпадається на цілі числа, так що float (x) / float (y) не можуть бути точно представлені як a float. Дивіться цей код:

import math

def geisler(x, y): return int(math.ceil(x / float(y))) * y

def orozco(x, y): return x + y * (x % y > 0) - x % y

def machin(x, y): return (x + y - 1) // y * y

for m, n in (
    (123456789123456789, 100),
    (1234567891234567891, 100),
    (12345678912345678912, 100),
    ):
    print; print m, "m"; print n, "n"
    for func in (geissler, orozco, machin):
        print func(m, n), func.__name__

Вихід:

123456789123456789 m
100 n
123456789123456800 geisler
123456789123456800 orozco
123456789123456800 machin

1234567891234567891 m
100 n
1234567891234568000 geisler <<<=== wrong
1234567891234567900 orozco
1234567891234567900 machin

12345678912345678912 m
100 n
12345678912345680000 geisler <<<=== wrong
12345678912345679000 orozco
12345678912345679000 machin

І ось кілька термінів:

>\python27\python -m timeit -s "import math;x =130" "int(math.ceil(x/100.0))*100"
1000000 loops, best of 3: 0.342 usec per loop

>\python27\python -m timeit -s "x = 130" "x + 100 * (x % 100 > 0) - x % 100"
10000000 loops, best of 3: 0.151 usec per loop

>\python27\python -m timeit -s "x = 100" "(x + 99) // 100 * 100"
10000000 loops, best of 3: 0.0903 usec per loop

I know the OP was about rounding an integer- але я хотів зазначити, що ви спробуєте використати ці 3 варіанти на (0,5,10), які я очікував би повернути 10, тоді перші два методи (geisler & orozco) повернули 10, як очікувалося, тоді як
machin

3

Якщо ваш int x: x + 100 - x % 100

Однак, як зазначено в коментарях, це поверне 200, якщо x==100.

Якщо це не очікувана поведінка, ви можете використовувати x + 100*(x%100>0) - x%100


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

Так, 100 не повинно бути округлено, але якщо це зробило б формулу занадто складною, я можу запобігти цьому, використовуючи код, не великий
userBG

Ну, інша версія вирішує це, оскільки включає перевірку перед додаванням 100! Якщо це відповідає вашим потребам, не забудьте прийняти! :)
Thomas Orozco

1
Вибачте, але я вважаю цей код дуже непитонічним. Так, a boolмає числове значення, так що так, ви можете помножити на логічний вираз. Але інші рішення є більш зрозумілими.
Мартін Гейслер

Ну, я справді зазначив, що можна віддавати перевагу іншому коду, якщо продуктивність не є ключовим параметром.
Thomas Orozco

3

Спробуйте це:

import math
def ceilm(number,multiple):
    '''Returns a float rounded up by a factor of the multiple specified'''
    return math.ceil(float(number)/multiple)*multiple

Зразок використання:

>>> ceilm(257,5)
260
>>> ceilm(260,5)
260

3

Попередження: Попереду передчасна оптимізація ...

Оскільки так багато відповідей тут роблять час цього, я хотів додати ще одну альтернативу.

Беручи @Martin Geisler

def roundup(x):
    return x if x % 100 == 0 else x + 100 - x % 100

(що мені найбільше подобається з кількох причин)

але враховуючи% дії

def roundup2(x):
    x100= x % 100
    return x if x100 == 0 else x + 100 - x100

Дає на ~ 20% покращення швидкості порівняно з оригіналом

def roundup3(x):
    x100 = x % 100
    return x if not x100 else x + 100 - x100

Це навіть краще і на ~ 36% швидше від оригіналу

нарешті, я думав, що можу скинути notоператора та змінити порядок гілок, сподіваючись, що це також збільшить швидкість, але збентежив, з’ясувавши, що це фактично повільніше зниження, щоб бути лише на 23% швидше від оригіналу.

def roundup4(x):
    x100 = x % 100
    return x + 100 - x100  if x100 else x


>python -m timeit -s "x = 130" "x if x % 100 == 0 else x + 100 - x % 100"
1000000 loops, best of 3: 0.359 usec per loop

>python -m timeit -s "x = 130" "x100 = x % 100"  "x if x100 == 0 else x + 100 - x100"
1000000 loops, best of 3: 0.287 usec per loop

>python -m timeit -s "x = 130" "x100 = x % 100"  "x if not x100 else x + 100 - x100"
1000000 loops, best of 3: 0.23 usec per loop

>python -m timeit -s "x = 130" "x100 = x % 100"  "x + 100 - x100 if x100 else x"
1000000 loops, best of 3: 0.277 usec per loop

пояснення, чому 3 швидше, ніж 4, було б дуже вітається.


0

Ось дуже просте рішення:

next_hundred = x//100*100+100

Як це працює?

  1. Виконайте ціле ділення на 100 (це в основному відсікає дробову частину нормального ділення). Таким чином ви отримуєте десятки числа. Наприклад: 243 // 100 = 2.
  2. Помножте на 100, отримавши вихідне число без його десятків і одиниць. Наприклад: 2 * 100 = 200.
  3. Додайте 100, щоб отримати бажаний результат. Наприклад: 200 + 100 = 300

Кілька прикладів

  • 0 ... 99 округлено до 100
  • 100 ... 199 округлено до 200
  • тощо

Трохи модифікований підхід раундів 1 ... 100-100, 101 ... 200-200, тощо:

next_hundred = (x-1)//100*100+100
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.