127

我在 Python 中发现了一些关于负数的奇怪行为:

>>> -5 % 4
3

谁能解释发生了什么?

4

10 回答 10

165

与 C 或 C++ 不同,Python 的模运算符 ( %) 总是返回一个与分母(除数)具有相同符号的数字。您的表达式产生 3 因为

(-5) / 4 = -1.25 --> 地板(-1.25) = -2

(-5) % 4 = (-2 × 4 + 3) % 4 = 3。

之所以选择它而不是 C 行为,是因为非负结果通常更有用。一个例子是计算工作日。如果今天是星期二(第 2 天),那么前N天是星期几?在 Python 中,我们可以计算

return (2 - N) % 7

但是在 C 语言中,如果N ≥ 3,我们得到一个负数,这是一个无效数,我们需要手动添加 7 来修复它:

int result = (2 - N) % 7;
return result < 0 ? result + 7 : result;

(请参阅http://en.wikipedia.org/wiki/Modulo_operator,了解如何确定不同语言的结果符号。)

于 2010-10-07T15:02:37.173 回答
39

这是 Guido van Rossum 的解释:

http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html

从本质上讲,a/b = q 和余数 r 保留了 b*q + r = a 和 0 <= r < b 的关系。

于 2010-10-07T15:06:49.620 回答
13

python中,模运算符是这样工作的。

>>> mod = n - math.floor(n/base) * base

所以结果是(对于你的情况):

mod = -5 - floor(-1.25) * 4
mod = -5 - (-2*4)
mod = 3

而其他语言,如C、JAVA、JavaScript使用截断而不是下限。

>>> mod = n - int(n/base) * base

这导致:

mod = -5 - int(-1.25) * 4
mod = -5 - (-1*4)
mod = -1

如果您需要有关 Python 中舍入的更多信息,请阅读

于 2020-02-12T06:58:25.573 回答
11

正如所指出的,Python 取模对其他语言的约定做了一个有充分理由的例外。

这为负数提供了无缝的行为,尤其是在与//整数除法运算符结合使用时,因为%模通常是(如在math.divmod 中):

for n in range(-8,8):
    print n, n//4, n%4

产生:

 -8 -2 0
 -7 -2 1
 -6 -2 2
 -5 -2 3

 -4 -1 0
 -3 -1 1
 -2 -1 2
 -1 -1 3

  0  0 0
  1  0 1
  2  0 2
  3  0 3

  4  1 0
  5  1 1
  6  1 2
  7  1 3
  • Python%总是输出零或正数*
  • Python//总是向负无穷大取整

* ...只要右操作数是正数。另一方面11 % -10 == -9

于 2015-03-06T14:18:53.637 回答
9

没有一种最好的方法来处理整数除法和带有负数的 mods。a/b如果的大小相同,符号相反,那就太好了(-a)/ba % b如果确实是模b ,那就太好了。既然我们真的想要a == (a/b)*b + a%b,前两者是不相容的。

保留哪一个是一个难题,双方都有争论。C 和 C++ 将整数除法四舍五入到零(so a/b == -((-a)/b)),显然 Python 没有。

于 2010-10-07T15:17:02.487 回答
4

其他答案,尤其是选定的答案,已经很好地回答了这个问题。但我想介绍一种可能更容易理解的图形方法,以及在 python 中执行普通数学模的 python 代码。

傻瓜的 Python 模数

模函数是一个方向函数,它描述了在我们在无限数的 X 轴上进行除法过程中的数学跳跃之后我们必须移动多少或向后移动多少。所以假设你在做7%3

在此处输入图像描述

所以在前向,你的答案是+1,但在后向-

在此处输入图像描述

你的答案是-2。这两者在数学上都是正确的。

同样,负数也有 2 个模数。例如:-7%3, 可以导致 -1 或 +2,如图所示 -

在此处输入图像描述

前进方向


在此处输入图像描述

向后的方向


在数学中,我们选择向内跳跃,即正数为正向,负数为反向。

但是在 Python 中,我们对所有正模运算都有一个前进方向。因此,您的困惑-

>>> -5 % 4 
3

>>> 5 % 4
1

这是python中向内跳转类型取模的python代码:

def newMod(a,b):
    res = a%b
    return res if not res else res-b if a<0 else res

这会给 -

>>> newMod(-5,4)
-1

>>> newMod(5,4)
1

很多人会反对内跳法,但我个人的看法是,这个更好!!

于 2021-04-17T14:33:39.733 回答
1

模数,4 的等价类:

  • 0:0、4、8、12...和-4、-8、-12...
  • 1:1、5、9、13...和-3、-7、-11...
  • 2:2、6、10... 和 -2、-6、-10...
  • 3:3、7、11... 和 -1、-5、-9...

这是一个指向负数模的行为的链接。(是的,我用谷歌搜索过)

于 2010-10-07T15:08:06.907 回答
1

我还认为这是 Python 的一种奇怪行为。事实证明,我没有很好地解决除法问题(在纸上);我给商的值为 0,给余数的值为 -5。太可怕了...我忘记了整数的几何表示。通过回忆数轴给出的整数的几何形状,可以得到商和余数的正确值,并检查 Python 的行为是否正常。(尽管我假设您很久以前就已经解决了您的问题)。

于 2016-11-09T16:50:20.570 回答
1

还值得一提的是,python 中的除法也与 C 不同:考虑

>>> x = -10
>>> y = 37

在 C 中,您期望结果

0

python中的x/y是什么?

>>> print x/y
-1

和 % 是模数 - 不是余数!而 C 中的 x%y 产量

-10

蟒蛇产量。

>>> print x%y
27

您可以像在 C 中一样获得两者

分工:

>>> from math import trunc
>>> d = trunc(float(x)/y)
>>> print d
0

其余的(使用上面的除法):

>>> r = x - d*y
>>> print r
-10

这种计算可能不是最快的,但它适用于 x 和 y 的任何符号组合,以获得与 C 中相同的结果,而且它避免了条件语句。

于 2019-05-22T15:00:50.773 回答
1

您可以使用:

result = numpy.fmod(x,y)

它将保留标志,请参阅numpy fmod() 文档

于 2021-11-13T15:40:19.457 回答