我知道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 //会 Select 向负无穷大进位呢?我没有找到任何资源来解释这一点,只是找到并听到人们含糊其辞地说:"for mathematics reasons".

例如,在100中:

抽象地处理这些事情的人往往会觉得

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

那么Python Select 向负无穷进位的(数学)原因是什么呢?


另见第Guido van Rossum's old blog post on the topic页.

推荐答案

但是为什么Python //会 Select 向负无穷大进位呢?

我不确定最初做出这一 Select 的the个原因是否在任何地方都有记录(尽管据我所知,它可以在某个地方的政治公众人物中详细解释),但我们肯定能找到各种理由来解释它的意义.

一个原因很简单,就是四舍五入朝着负(或正!)无穷大意味着所有数字都以相同的方式取整,而向零取整则使零变得特别.数学上的说法是四舍五入−∞ 为translation invariant,即满足以下等式:

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

对于所有实数x和所有整数k.例如,四舍五入不等于零:

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

当然,也存在其他参数,例如您引用的参数基于与%运算符(行为)的兼容性(我们希望如何),下面将对此进行详细介绍.

实际上,我想说的是,这里的real个问题是,为什么Python的int()函数被定义为not,将浮点参数向负无穷大取整,这样m // n就等于int(m / n).(我怀疑是"历史原因".)再说一次,这也没什么大不了的,因为Python至少有math.floor()个满足m // n == math.floor(m / n)个.


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

没错,但在/轮接近零的情况下保持这种身份,需要以一种尴尬的方式定义%为负数.特别是,我们失go 了Python %的以下两个有用的数学属性:

  1. 0 <= m % n < n表示所有m和所有正n;和
  2. (m + k * n) % n == m % n表示所有整数mnk.

这些属性很有用,因为%的主要用途之一是将数字m"环绕"到长度n的有限范围内.


例如,假设我们试图计算方向:假设heading是我们当前的compass heading度(从正北顺时针方向算起,0 <= heading < 360度),我们想在转动angle度后计算新的航向(顺时针转动angle > 0度,逆时针转动angle < 0度).使用Python的%运算符,我们可以简单地计算新标题:

heading = (heading + angle) % 360

这将在所有情况下都有效.

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

为了使用C++ %操作符来获得正确的环绕行为,我们需要将公式写入如下:

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

或作为:

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

(只有在我们始终能够保证heading + angle >= -360的情况下,更简单的公式heading = (heading + angle + 360) % 360才会起作用.)

这是为除法使用非平移不变舍入规则,并因此使用非平移不变%运算符所付出的代价.

Python-3.x相关问答推荐

如何从枚举中获取某个值?

Python3:是否可以将变量用作函数调用的一部分

仅当从 USB 摄像头接收到新图像时才处理图像

attrs 将 list[str] 转换为 list[float]

多进程:两个进程,一起杀死

根据另一列值对多个数据框列进行分组

如何在带有 GUI 的 python 游戏中设置回答时间限制?

Pytorch:图像标签

没有可重定向到的 URL.提供一个 url 或在模型上定义一个 get_absolute_url 方法

逗号分隔列表的 argparse 操作或类型

创建一个可旋转的 3D 地球

python - 错误 R10(启动超时)-> Web 进程未能在启动后 60 秒内绑定到 $PORT

__new__ 方法给出错误 object.__new__() 只接受一个参数(要实例化的类型)

Python heapify() 时间复杂度

ImportError:无法在 PyQt5 中导入名称QStringList

Python的max函数有多高效

python setup.py egg_info mysqlclient

计算两个文件的行差异的最有效方法是什么?

将列表列表转换为Python中的字典字典

如何修复:cx_Oracle.DatabaseError:DPI-1047:找不到 64 位 Oracle 客户端库 - Python