18

你能告诉我多少钱(-2) % 5吗?根据我的 Python 解释器是 3,但是您对此有明智的解释吗?

我已经读过,在某些语言中,结果可能取决于机器,但我不确定。

4

12 回答 12

16

顺便说一句:大多数编程语言都会不同意 Python 并给出结果-2。根据模数的解释,这是正确的。然而,最一致的数学定义表明ab的模数是a / b除法的(严格正数)余数r。更准确地说,根据定义,0 <= r < b

于 2008-09-04T13:46:23.663 回答
14

您的 Python 解释器是正确的。计算模数的一种(愚蠢的)方法是减去或添加模数,直到结果值介于 0 和(模数 - 1)之间。

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

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

于 2008-09-04T13:40:59.853 回答
14

对负数进行模运算的结果似乎取决于编程语言,这里是一个列表http://en.wikipedia.org/wiki/Modulo_operation

于 2008-09-04T13:41:25.640 回答
6

就像文档在二进制算术运算中所说的那样,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)
于 2010-07-11T22:18:00.473 回答
4

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

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

按照这个逻辑,-2 必须是 3。

思考它如何工作的最简单方法是不断加或减 5,直到数字落在 0(包括)和 5(不包括)之间。

我不确定机器依赖性 - 我从未见过这样的实现,但我不能说它从未完成过。

于 2008-09-04T13:41:40.257 回答
3

正如其他答案中所解释的,负值的模运算有很多选择。一般来说,不同的语言(和不同的机器架构)会给出不同的结果。

根据Python 参考手册

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

是 Python 的选择。基本上定义了模数,以便它始终成立:

x == (x/y)*y + (x%y)

所以 (-2)%5 = -2 - (-2/5)*5 = 3 是有道理的

于 2008-09-04T14:25:05.973 回答
0

好吧,-2 除以 5 将是 0,余数为 3。我不认为这应该非常依赖于平台,但我见过更奇怪的事情。

于 2008-09-04T13:41:04.253 回答
0

确实是 3。在模算术中,模只是除法的余数,-2 除以 5 的余数是 3。

于 2008-09-04T13:41:19.573 回答
0

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

于 2008-09-04T13:53:07.093 回答
0

一种解释可能是使用2 的补码存储负数。当 python 解释器尝试进行模运算时,它会转换为无符号值。因此,它实际上不是执行 (-2) % 5 而是计算 0xFFFF_FFFF_FFFF_FFFD % 5 ,即 3。

于 2008-09-04T14:12:16.470 回答
0

注意不要在所有操作系统和架构上依赖 C/C++ 中的这种 mod 行为。如果我没记错的话,我尝试依赖 C/C++ 代码,例如

float x2 = x % n;

将 x2 保持在 0 到 n-1 的范围内,但是当我在一个操作系统上编译时会出现负数,但在另一个操作系统上会正常工作。这造成了一次邪恶的调试,因为它只发生了一半!

于 2008-09-04T14:46:11.227 回答
0

术语“模数”和“余数”之间似乎存在常见的混淆。

在数学中,余数的定义应始终与商一致,因此 if a / b == c rem dthen (c * b) + d == a。根据您如何四舍五入您的商,您会得到不同的余数。

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

某些语言(尤其是 C 和 C++)没有定义所需的舍入/余数行为并且%是模棱两可的。许多人将舍入定义为接近零,但在余数更正确的情况下使用术语模数。Python 相对不寻常,因为它舍入到负无穷大,所以模数和余数是等价的。

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

C 策略旨在允许编译器为机器选择最有效的实现,但 IMO 是错误的优化,至少现在是这样。一个好的编译器可能能够在不能出现负数的情况下使用等价进行优化(几乎可以肯定,如果您使用无符号类型)。另一方面,在可能出现负数的情况下,您几乎肯定会关心细节——出于可移植性的原因,您必须使用非常精心设计的过度复杂算法和/或检查,以确保无论四舍五入和余数如何都能得到您想要的结果行为。

换句话说,这种“优化”的收益主要(如果不总是)是一种错觉,而在某些情况下会有非常真实的成本 - 所以这是一种错误的优化。

于 2011-03-05T11:16:46.253 回答