如何在Python中实现向上取整的除法

8 投票
8 回答
7384 浏览
提问于 2025-04-17 00:17

我想让3除以2等于2,而不是1.5。

我知道这个操作有个数学术语(不是叫向上取整),但我现在想不起来了。
总之,我想知道怎么做到这一点,而不需要用两个函数。

我不想要的例子:

answer = 3/2 then math.ceil(answer)=2 (why does math.ceil(3/2)=1?)  

我想要的例子:

 "function"(3/2) = 2

8 个回答

4

提问者的意思是“如何在Python中实现向上取整的除法”(建议你改一下标题)。

这种取整方式是完全合理的,符合IEEE-754标准(可以看看这个概述round(float(a)/b),或者创建一个numbers.Number的子类并重写__div__()

提问者需要澄清一下,他们希望-3/2取整到-2还是-1(或者对负数不在乎)。因为他们已经说过不想向上取整,所以我们可以推测-3/2应该取整到-2。

说了这么多理论,接下来是实现方法:

.

import numbers

class NumberWithRounding(numbers.Integral):
    # Here you could implement a classmethod setRoundingMode() or member rounding_mode
    def __div__(self,other):
        # here you could consider value of rounding_mode, or else hardwire it like:
        return round(float(self)/other)
    # You also have to raise ImplementationError/ pass/ or implement the other 31
    # methods for Float: __abs__(),...,__xor__() Just shortcut that for now...
4

在Python 3中,整数除法的结果是:

3 // 2 == 1

在Python 3中,非整数除法的结果是:

3 / 2 == 1.5

你所说的并不算是除法的全部情况。

16

简单回答一下...

Python 只提供了两种除法运算的原生操作符:“真实”除法和“向下取整”除法。所以你想要的功能并没有一个单独的函数。不过,使用一些简单的表达式,我们可以很容易地实现多种不同类型的带舍入的除法。

根据标题的要求:给定严格的整数输入,可以使用 (a+(-a%b))//b 来实现“向上取整”除法,而“远离零”除法可以用更复杂的 a//b if a*b<0 else (a+(-a%b))//b 来实现。这两种方法中,可能有一种是你想要的。至于为什么...


再详细一点...

首先,让我回答一个小问题,为什么 3/2==1math.ceil(3/2)==1.0,这要从 Python 的除法运算符说起。这里有两个主要的问题...

floatint 除法:在 Python 2 中,除法的行为取决于输入的类型。如果 ab 都是整数,a/b 会执行“向下取整”或“向下取整整数”除法(例如 3/2==1,但 -3/2==-2)。这相当于 int(math.floor(float(a)/b))

但是如果 ab 中至少有一个是浮点数,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 版本中表现相同。此外,我假设如果 b 是正数,则 0<=a%b<b,如果 b 是负数,则 b<=a%b<=0。这是 Python 的行为,但其他语言的取模运算符可能会稍有不同。

带舍入的四种基本整数除法类型:

  • “向下取整”,也叫“向下取整整数”或“取整到负无穷”除法:Python 原生支持这个,通过 a//b 实现。

  • “向上取整”,也叫“向上取整整数”或“取整到正无穷”除法:可以通过 int(math.ceil(float(a)/b))(a+(-a%b))//b 来实现。后者的公式之所以有效,是因为当 ab 的倍数时,-a%b 为 0,否则就是我们需要加到 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 提供的向上取整/远离零的效率甚至比上述任何解决方案都要高,但对于负数就不适用了。

希望这能帮到你...如果有人能建议更好的向零或远离零的取整公式,我很乐意进行修改。我觉得我现在的公式特别不满意。

撰写回答