Подтвердить что ты не робот

Круглый с целым делением

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Если вы действительно отвлекаетесь на скорость, делайте тесты. В противном случае, KISS.

Ответ 2

skip = (((total << 1) // surplus) + 1) >> 1

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

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

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

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

Ответ 3

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

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)

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

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

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

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

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

Ответ 4

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

q, r = divmod(total, surplus)
skip = q + int(bool(r))

Ответ 5

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

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

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

Ответ 6

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

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