【问题标题】:The modulo operation on negative numbers in PythonPython中负数的模运算
【发布时间】:2011-04-22 10:22:08
【问题描述】:

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

>>> -5 % 4
3

谁能解释一下是怎么回事?

【问题讨论】:

标签: python modulo negative-number


【解决方案1】:

与 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,了解如何确定不同语言的结果符号。)

【讨论】:

【解决方案2】:

下面是 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

【讨论】:

  • 像 C++ 和 Java 这样的语言也保留了第一个关系,但它们的上限为负数 a,正数b,而 Python 楼层数。 abs(r) &lt; b 总是正确的,如果 r &lt;= 0 是他们的上限。
【解决方案3】:

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 中舍入的更多信息,请阅读this

【讨论】:

    【解决方案4】:

    正如所指出的,Python 取模对其他语言的约定产生了well-reasoned 例外。

    这为负数提供了一种无缝的行为,尤其是与// 整数除法运算符结合使用时,因为% 模通常是(如在数学中。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

    【讨论】:

    • 谢谢你的例子让我明白了:)
    【解决方案5】:

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

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

    【讨论】:

    • "如果 a/b 与 (-a)/b 的大小相同但符号相反,那就太好了。"为什么会很好?这是什么时候需要的行为?
    • 因为它的行为方式与常规除法和乘法相同,因此直观上易于使用。不过,这在数学上可能没有意义。
    【解决方案6】:

    其他答案,尤其是选定的答案,已经很好地回答了这个问题。但我想介绍一种可能更容易理解的图形方法,以及在 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
    

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

    【讨论】:

    • 感谢可视化,它真的很有帮助。想补充一下“但是在 Python 中,我们对所有模运算都有一个前进的方向。”。 7 % -3 或 7 % -3 怎么样?这不是倒退吗?
    • @Alex 是的,你是对的,我的意思是“对于所有正模运算”。
    【解决方案7】:

    模数,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...

    这是modulo's behavior with negative numbers 的链接。 (是的,我用谷歌搜索过)

    【讨论】:

    • @NullUserException - 是的,确实如此。固定的。谢谢。
    • 链接好像失效了
    • @Astariul 互联网的大问题。如果您有其他建议,我完全赞成。也就是说,这是一个 11 年前的帖子!
    • 我不明白你的解释
    【解决方案8】:

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

    【讨论】:

      【解决方案9】:

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

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

      在 C 中你期望得到结果

      0
      

      python 中的 x/y 是什么?

      >>> print x/y
      -1
      

      并且 % 是模数 - 不是余数!而 C 中的 x%y 产生

      -10
      

      python 产生。

      >>> 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 中相同的结果,而且它避免了条件语句。

      【讨论】:

        【解决方案10】:

        你可以使用:

        result = numpy.fmod(x,y)
        

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

        【讨论】:

          猜你喜欢
          • 2021-03-20
          • 2015-07-23
          • 2012-12-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多