Округление с целочисленным делением - PullRequest
23 голосов
/ 16 октября 2010

Существует ли простой, питонный способ округления до ближайшего целого числа без использования плавающей запятой? Я хотел бы сделать следующее, но с целочисленной арифметикой:

skip = int(round(1.0 * total / surplus))

==============

@ Джон: Плавающая точка не воспроизводится на разных платформах. Если вы хотите, чтобы ваш код проходил тесты на разных платформах, вам следует избегать использования чисел с плавающей запятой (или добавить некоторые хакерские штучки в свои тесты и надеяться, что это сработает). Вышеупомянутое может быть достаточно простым, чтобы оно было одинаковым на большинстве / всех платформах, но я бы предпочел не делать этого определения, так как легче вообще избежать с плавающей запятой. Как это "не в духе Python"?

Ответы [ 6 ]

35 голосов
/ 17 октября 2010

Вы можете сделать это довольно просто:

(n + d // 2) // d, где n - дивиденд, а d - делитель.

Альтернативы, такие как (((n << 1) // d) + 1) >> 1 или эквивалент (((n * 2) // d) + 1) // 2 может быть МЕДЛЕННЫМ в последних CPythons, где int реализован как старый long.

Простой метод делает 3 переменных доступа, 1 постоянную загрузку и 3 целочисленных операции.Сложные методы делают 2 переменных доступа, 3 постоянных загрузки и 4 целочисленных операции.Целочисленные операции, вероятно, потребуют времени, которое зависит от размеров участвующих чисел.Доступ к переменным локальным функциям не требует «поиска».

Если вы действительно не любите скорость, сделайте тесты.Иначе ПОЦЕЛУЙ.

6 голосов
/ 08 апреля 2014

Вдохновленный ответом Жмых ответом, который

q, r = divmod(total, surplus)
skip = q + int(bool(r)) # rounds to next greater integer (always ceiling)

, я пришел к следующему решению:

q, r = divmod(total, surplus) 
skip = q + int(2 * r >= surplus) # rounds to nearest integer (floor or ceiling)

Поскольку ОП запросил округлениедо ближайшего целого числа, решение zhmhs на самом деле немного неверно, потому что оно всегда округляется до следующего следующего большего целого числа, в то время как мое решение работает так, как требуется.

(Если вы считаете, что мой ответ должен был быть редактированием или комментарием к ответу zhmh, позвольте мне указать, что мое предлагаемое изменение для него было отклонено, поскольку это должен был быть комментарий,но у меня пока недостаточно репутации для комментирования!)

Если вам интересно, как определяется divmod: в соответствии с документацией

Для целых чисел результат такой же, как (a // b, a % b).

Поэтому мы придерживаемся целочисленной арифметики, как того требует ОП.

6 голосов
/ 16 октября 2010
skip = (((total << 1) // surplus) + 1) >> 1

Сдвиг вещей, оставленных на один бит, эффективно умножается на два, сдвиг вещей на один бит делит на два округления вниз.Добавление одного в середине делает так, чтобы «округление вниз» фактически округлялось вверх, если бы результат был выше десятичной части .5 *

Это в основном так же, как если бы вы написали ...

skip = int((1.0*total/surplus) + 0.5)

за исключением того, что все умножается на 2, а затем делится на 2, что можно делать с целочисленной арифметикой (поскольку сдвиги битов не требуют с плавающей запятой).

2 голосов
/ 25 октября 2012

Еще один забавный способ:

q, r = divmod(total, surplus)
skip = q + int(bool(r))
0 голосов
/ 17 октября 2010

Просто позаботьтесь о правиле округления, прежде чем делить. Для простейшего раунда:

if total % surplus < surplus / 2:
    return total / surplus
else:
    return (total / surplus) + 1

Немного подкорректируйте, если вам нужно сделать правильное округление до четного.

0 голосов
/ 17 октября 2010

Это тоже должно работать:

def rint(n):
    return (int(n+.5) if n > 0 else int(n-.5))
...