负值模运算 - 奇怪的事情?[复制]

Modulus operation with negatives values - weird thing? [duplicate]

提问人:lurks 提问时间:9/4/2008 最后编辑:Nayukilurks 更新时间:9/16/2015 访问量:12354

问:

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

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

Python 数学

评论

0赞 0x2b3bfa0 1/29/2017
您可以使用来获得与 C 或 Java 中相同的行为。math.fmod
0赞 Karl Knechtel 1/12/2023
Python 没有内置功能的机器相关结果。它确实具有与版本相关的结果和特定于平台的库。

答:

14赞 Tnilsson 9/4/2008 #1

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

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

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

评论

1赞 augurar 8/21/2014
这取决于整数除法的定义方式。如果除法向 0 四舍五入,则余数应与商具有相同的符号,因此 .如果除法四舍五入到负无穷大(如在 Python 中),则余数应始终为正数。b * (a/b) + a%b == a
0赞 Jason Baker 9/4/2008 #2

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

评论

3赞 tzot 7/12/2010
你的意思是:“-2 除以 5 就是 -1,余数为 3”,对吧?无论如何,这就是 Python 所做的。
0赞 augurar 8/21/2014
在某些系统中,-2 除以 5 等于 0,余数为 -2。在其他情况下,它是 -1,余数为 3。只是品味问题,没有“正确”的答案。
0赞 Matt Dillard 9/4/2008 #3

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

评论

0赞 augurar 8/21/2014
在表达式中,是模数,而不是 。c = a%bbc
14赞 martinatime 9/4/2008 #4

负数模运算的结果似乎与编程语言有关,这里是一个列表 http://en.wikipedia.org/wiki/Modulo_operation

4赞 Matt Sheppard 9/4/2008 #5

好吧,0 % 5 应该是 0,对吧?

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

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

思考它如何工作的最简单方法是不断添加或减去 5,直到数字介于 0(含)和 5(不含)之间。

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

16赞 Konrad Rudolph 9/4/2008 #6

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

0赞 Ozgur Ozcitak 9/4/2008 #7

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

0赞 Ionut Anghelcovici 9/4/2008 #8

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

3赞 dF. 9/4/2008 #9

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

根据 Python 参考手册

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

是 Python 的选择。基本上,模是定义的,因此它始终成立:

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

因此,(-2)%5 = -2 - (-2/5)*5 = 3 是有道理的

0赞 Jared Updike 9/4/2008 #10

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

float x2 = x % n;

将 x2 保持在 0 到 n-1 的范围内,但是当我在一个操作系统上编译时,负数悄悄出现,但在另一个操作系统上一切正常。这导致了糟糕的调试时间,因为它只发生了一半的时间!

6赞 tzot 7/12/2010 #11

就像文档在二进制算术运算中所说的那样,Python 确保:

整数除法运算符和模运算符通过以下标识连接:。整数除法和取模也与内置函数 divmod() 连接: 。x == (x/y)*y + (x%y)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)
0赞 user180247 3/5/2011 #12

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

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

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

某些语言(特别是 C 和 C++)没有定义所需的舍入/余数行为,并且不明确。许多人将四舍五入定义为接近零,但使用术语模,而余数更正确。Python 相对不寻常,因为它四舍五入到负无穷大,因此模数和余数是等价的。%

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

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

换句话说,这种“优化”的收益大多(如果不是总是)是一种错觉,而在某些情况下存在非常真实的成本——所以这是一个虚假的优化。