Чтобы дать краткий ответ ...
Python предлагает только нативные операторы для двух типов деления: «истинное» деление и «округление вниз».То, что вы хотите, не доступно как отдельная функция.Тем не менее, можно легко реализовать несколько различных типов деления с округлением, используя некоторые короткие выражения.
По запросу заголовка: с учетом строго целочисленных входных данных деление «округление вверх» может быть реализовано с использованием (a+(-a%b))//b
, а деление «округление от нуля» может быть реализовано с использованием более сложного a//b if a*b<0 else (a+(-a%b))//b
.Один из них, вероятно, то, что вы хотите.Что касается того, почему ...
Чтобы дать более длинный ответ ...
Сначала позвольте мне ответить на вопрос о том, почему 3/2==1
и math.ceil(3/2)==1.0
, чтобы объяснить, как работает оператор деления Python.В игре есть две основные проблемы ...
float
против int
деление: В Python 2 деление ведет себя по-разному в зависимости от типа входов.Если оба значения a
и b
являются целыми числами, a/b
выполняет деление "округление вниз" или "целое число по полу" (например, 3/2==1
, но -3/2==-2
).Это эквивалентно int(math.floor(float(a)/b))
.
Но если хотя бы один из a
и b
являются числами с плавающей запятой, Python выполняет "истинное" деление и выдает результат float
(например, 3.0/2==1.5
и -3.0/2==-1.5
).Вот почему вы иногда увидите конструкцию float(a)/b
: она используется для принудительного деления, даже если оба входа являются целыми числами (например, float(3)/2==1.5
).Вот почему ваш пример math.ceil(3/2)
возвращает 1.0
, тогда как math.ceil(float(3)/2)
возвращает 2.0
.Результат уже был округлен до того, как он достиг math.ceil()
.
«истинное деление» по умолчанию : в 2001 году было принято решение ( PEP 238 )что оператор деления Python должен быть изменен так, чтобы он всегда выполнял «истинное» деление, независимо от того, являются ли входные значения числами с плавающей запятой или целыми числами (например, это составит 3/2==1.5
).Чтобы не нарушать существующие скрипты, изменение поведения по умолчанию было отложено до Python 3.0;чтобы получить такое поведение в Python 2.x, вы должны включить его для каждого файла, добавив from __future__ import division
в начало файла.В противном случае используется старое поведение, зависящее от типа.
Но «округление» деление все еще часто необходимо, поэтому PEP не справился с этим полностью.Вместо этого он ввел новый оператор деления: a//b
, который всегда выполняет деление с округлением вниз, даже если входные данные включают числа с плавающей запятой.Это можно использовать, не делая ничего особенного в Python 2.2+ и 3.x.
Таким образом, деление с округлением:
Для упрощения, все следующие выражения используют оператор a//b
, когдаработает над целыми числами, так как он будет вести себя одинаково во всех версиях Python.Кроме того, я предполагаю, что 0<=a%b<b
, если b
положительно, и b<=a%b<=0
, если b отрицательно.Вот как ведет себя Python, но другие языки могут иметь немного другие операторы модуля.
Четыре основных типа целочисленного деления с округлением:
"округление вниз" aka "целое число на полу" aka "округление до минус бесконечности"divsion: python предлагает это изначально через a//b
.
округление вверх ака «целочисленное значение потолка» ака «округление до положительной бесконечности» деление: это может быть достигнуто с помощью int(math.ceil(float(a)/b))
или (a+(-a%b))//b
.Последнее уравнение работает, потому что -a%b
равно 0, если a
кратно b
, и в противном случае это сумма, которую мы должны добавить к a
, чтобы добраться до следующего наивысшего кратного.
«округление до нуля» или «усеченное» деление - это может быть достигнуто с помощью int(float(a)/b)
.Делать это без использования чисел с плавающей запятой сложнее ... поскольку Python предлагает только целочисленное деление с округлением вниз, а оператор %
имеет аналогичное смещение с округлением вниз, у нас нет операторов без плавающей запятой, которые округляются симметричноо 0. Таким образом, единственный способ, которым я могу думать, состоит в том, чтобы построить кусочное выражение из округления в меньшую сторону и округления в большую сторону: a//b if a*b>0 else (a+(-a%b))//b
.
«округление от нуля» или «округление до (любой) бесконечности» - к сожалению, это даже сложнее, чем округление к нулю.Мы больше не можем использовать усечающее поведение оператора int
, поэтому я не могу думать о простом выражении даже при включении операций с плавающей точкой.Поэтому я должен пойти с обратным выражением с округлением до нуля и использовать a//b if a*b<0 else (a+(-a%b))//b
.
Обратите внимание, что если вы используете только натуральные числа, (a+b-1)//b
обеспечиваетокруглять / убывать от нуля даже более эффективно, чем любое из вышеприведенных решений, но разваливается для негативов.
Надеюсь, что это поможет ... и с удовольствием внесу изменения, если кто-нибудь сможет предложить более подходящие уравнения для округления до / противс нуля.Я нахожу те, которые у меня есть, особенно неудовлетворительными.