负数模运算——奇怪的事情?

18

请问(-2) % 5的计算结果是多少?据我的Python解释器显示为3,但您能不能对此进行简明扼要的解释呢?

我读过一些关于某些语言中结果可能依赖于机器的说法,但我不确定这是否正确。


你可以使用 math.fmod 来获得与 C 或 Java 相同的行为。 - 0x2b3bfa0
Python内置功能没有机器相关的结果。它确实有版本相关的结果和特定于平台的库。 - Karl Knechtel
12个回答

16

顺便说一下:大多数编程语言会与Python不同,并显示结果-2。根据模运算的解释,这是正确的。然而,大多数人认可的数学定义规定,ab的模是它们相除产生的(严格为正的)余数r。更准确地说,根据定义,0 <= r < b


14

你的Python解释器是正确的。

一种(愚蠢的)计算模数的方法是不断加减模数,直到结果的值在0和(模数-1)之间。

例如:13 mod 5 = (13 − 5) mod 5 = (13 − 10) mod 5 = 3

或者在你的情况下:-2 mod 5 = (-2 + 5) mod 5 = 3


1
这取决于整数除法的定义方式。如果除法向0舍入,则余数应与商具有相同的符号,以便 b * (a/b) + a%b == a。如果除法向负无穷舍入(如Python中所示),则余数应始终为正数。 - augurar

14

6

如Python文档在二进制算术运算中所述,Python确保:

整数除法和取模运算符通过以下恒等式相互关联:x == (x/y)*y + (x%y)。 整数除法和取模还与内置函数divmod()相关联:divmod(x, y) == (x/y, x%y)

事实上,

>>> divmod(-2, 5)
(-1, 3).

另一种展示该方法统一性的方式是计算一小组数字的divmod

>>> for number in xrange(-10, 10):
...     print divmod(number, 5)
...
(-2, 0)
(-2, 1)
(-2, 2)
(-2, 3)
(-2, 4)
(-1, 0)
(-1, 1)
(-1, 2)
(-1, 3)
(-1, 4)
(0, 0)
(0, 1)
(0, 2)
(0, 3)
(0, 4)
(1, 0)
(1, 1)
(1, 2)
(1, 3)
(1, 4)

4

那么,0%5应该是0,对吗?

-1%5应该是4,因为这是沿相反方向的下一个允许数字(即不能是5,因为它超出了范围)。

按照这个逻辑进行下去,-2必须是3。

最简单的想法是,你不断地加上或减去5,直到数字落在0(包括)和5(不包括)之间。

我不确定机器的依赖性 - 我从来没有见过这样的实现,但我不能说它从来没有被做过。


3
如其他答案所述,对于负数的模运算有很多选择。通常不同的编程语言(和不同的机器架构)会给出不同的结果。
根据Python参考手册

模运算符始终生成与其第二个操作数(或零)相同符号的结果;结果的绝对值严格小于第二个操作数的绝对值。

这是Python所采用的选择。基本上,模运算被定义为始终满足以下条件:
x == (x/y)*y + (x%y)

所以,(-2)%5 = -2 - (-2/5)*5 = 3。

0

在编程中,“模数”和“余数”的术语经常会引起混淆。

在数学中,余数应始终与商一致定义,因此如果 a / b == c rem d,则 (c * b) + d == a。根据商的舍入方式不同,得到的余数也不同。

然而,模数应始终给出一个结果 0 <= r < divisor,这只有在允许负整数时才与向负无穷大除法一致。如果除法向零舍入(这很常见),则模数和余数仅对非负值等效。

一些语言(特别是 C 和 C++)没有定义所需的舍入/余数行为,% 是不明确的。许多语言将舍入定义为向零舍入,但使用模数一词,而余数更正确。Python 相对不寻常,因为它向负无穷大舍入,所以模数和余数是等效的。

Ada 向零舍入,但具有 modrem 运算符。

C语言的策略旨在让编译器为机器选择最有效的实现方式,但在我看来这是一种错误的优化,至少在现今时代如此。一个好的编译器可能会能够在负数无法出现的任何地方使用等价性进行优化(而且几乎肯定会使用无符号类型)。另一方面,在负数可能出现的情况下,你几乎肯定关心细节 - 由于可移植性原因,你必须使用设计非常谨慎的过分复杂的算法和/或检查来确保你获得想要的结果,无论舍入和余数行为如何。

换句话说,这种“优化”的收益大多数情况下(如果不总是)都是一种错觉,而在某些情况下有非常真实的成本 - 因此它是一种虚假的优化。


0

嗯,-2除以5的结果是0,余数为3。我不认为这应该与平台有太大关系,但我见过更奇怪的事情。


3
您的意思是:"2除以5,商为-1,余数为3",对吗?这就是Python的计算方式。 - tzot
在某些系统中,-2除以5的结果是0,余数为-2。而在其他系统中,它是-1,余数为3。这只是一种口味上的选择,没有“正确”的答案。 - augurar

0

确实是3。在模运算中,模数就是除法的余数,而-2÷5的余数是3。


在表达式 c = a%b 中,b 是模数,而不是 c - augurar

0

结果取决于编程语言。Python 返回除数的符号,而例如 c# 返回被除数的符号(即 -2 % 5 在 c# 中返回 -2)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接