Python选择将整数除法舍入到负无穷大背后的数学原因是什么?

我知道Python//舍入到负无穷大,而在C++中/正在截断,舍入到0。

到目前为止我知道的是:

               |remainder|
-12 / 10  = -1,   - 2      // C++
-12 // 10 = -2,   + 8      # Python

12 / -10  = -1,     2      // C++
12 // -10 = -2,   - 8      # Python

12 / 10  = 1,      2       // Both
12 // 10 = 1,      2

-12 / -10 = 1,    - 2      // Both
          = 2,    + 8

C++:
1. m%(-n) == m%n
2. -m%n == -(m%n)
3. (m/n)*n + m%n == m

Python:
1. m%(-n) == -8 == -(-m%n)
2. (m//n)*n + m%n == m
但是,为什么Python//选择向负无穷大取整?我没有找到任何资源来解释这一点,只是找到并听到人们含糊地说:&;因为数学原因。

例如Why is -1/2 evaluated to 0 in C++, but -1 in Python?:

抽象地处理这些事情的人往往会觉得 舍入到负无穷大更有意义(这意味着 与数学中定义的模函数兼容,而不是 意思有点滑稽)。

但我认为C++的/与模函数不兼容。在C++中,(m/n)*n + m%n == m也适用。

那么,是什么(数学)原因让Python选择舍入到负无穷大呢?


另请参阅Guido van Rossum's old blog post on the topic。


解决方案

但为什么Python//选择舍入为负无穷大?

我不确定最初做出这个选择的原因是否有任何记录(尽管,就我所知,它可以在某个PEP中得到很长的解释),但我们肯定可以想出各种理由来解释它的意义。

一个原因很简单,就是舍入为负(或正!)无穷大意味着所有数字都以相同的方式进行舍入,而向零进行舍入则使零变得特别。这样说的数学方式是,向下舍入到?∞是平移不变量,即它满足等式:

round_down(x + k) == round_down(x) + k

对于所有实数x和所有整数k。向零四舍五入不会,因为,例如:

round_to_zero(0.5 - 1) != round_to_zero(0.5) - 1

当然,也存在其他参数,例如您引用的基于与%运算符(行为的方式)的兼容性的参数-下面将详细介绍。

事实上,我想说真正的问题是,为什么要定义的int()函数而不是,将浮点参数舍入到负无穷大,以便m // n等于int(m / n)。(我怀疑是因为历史原因。)话又说回来,这不是什么大不了的事,因为至少有math.floor()满足m // n == math.floor(m / n)


但我认为C++的/与模函数不兼容。在C++中,(m/n)*n + m%n == m也适用。

是正确的,但在将/四舍五入到零的同时保留该身份需要以一种尴尬的方式为负数定义%。特别是,我们失去了以下两个有用的Python%数学属性:

  1. 0 <= m % n < n全部m和全部正面n;和
  2. (m + k * n) % n == m % n适用于所有整数mnk

这些属性非常有用,因为%的主要用法之一是将数字m换行到有限的长度范围。


例如,假设我们正在尝试计算方向:假设heading是我们当前的compass heading度(从正北开始顺时针计算,其中0 <= heading < 360),并且我们想要在旋转angle度后计算新的航向(其中angle > 0如果我们顺时针转,angle < 0如果我们逆时针转)。使用Python的%运算符,我们可以简单地将新标题计算为:

heading = (heading + angle) % 360

这在所有情况下都适用。

然而,如果我们尝试在C++中使用这个公式,它具有不同的舍入规则和相应的不同的%运算符,我们会发现回绕并不总是像预期的那样工作!例如,如果我们开始朝向西北(heading = 315),然后顺时针旋转90°(angle = 90),我们最终确实会朝向东北(heading = 45)。但如果然后尝试逆时针旋转90°(angle = -90),使用C++的%运算符,我们将不会像预期的那样返回到heading = 315,而是返回到heading = -45

要使用C++%运算符获得正确的回绕行为,我们需要将公式写成如下所示:

heading = (heading + angle) % 360;
if (heading < 0) heading += 360;

或AS:

heading = ((heading + angle) % 360) + 360) % 360;

(更简单的公式heading = (heading + angle + 360) % 360只有在我们始终保证heading + angle >= -360的情况下才有效。)

这是您为拥有非平移不变的除法舍入规则以及相应的非平移不变的%运算符而付出的代价。

相关文章