【问题标题】:C and Python - different behaviour of the modulo (%) operationC 和 Python - 模 (%) 运算的不同行为
【发布时间】:2010-12-26 19:49:49
【问题描述】:

我发现相同的 mod 操作会根据所使用的语言产生不同的结果。

在 Python 中:

-1 % 10

产生9

在 C 中它产生 -1

  1. 哪个是正确的模数?
  2. 如何让 C 中的 mod 操作和 Python 中的一样?

【问题讨论】:

    标签: python c math modulo


    【解决方案1】:
    1. 这两种变体都是正确的,但是在数学(尤其是数论)中,Python 的modulo 是最常用的。
    2. 在 C 中,您执行 ((n % M) + M) % M 以获得与 Python 中相同的结果。例如。 ((-1 % 10) + 10) % 10。请注意,它仍然适用于正整数:((17 % 10) + 10) % 10 == 17 % 10,以及 C 实现的两种变体(正余数或负余数)。

    【讨论】:

    • n = - 11 怎么样?我想你的意思是 ((n % M) + M) % M
    • 与 (-17 + 10) % 10 你遇到了同样的问题。
    • 我必须不同意第 1 点,并说在数学中两者都是正确的,因为它定义了一个同余类。
    • @Checkers 不太正确,在 C 中它没有定义真正的同余关系,因为 -1 % 10 != 9 % 10 并且它们显然属于同一个同余类。
    • @fortran 这就是为什么在 C 中你必须推出自己的实现来定义一个,不是吗?
    【解决方案2】:

    Python 有一个“真正的”模运算,而 C 有一个 余数 运算。

    它与负整数除法的处理方式有直接关系,即向 0 舍入或负无穷大。 Python 向负无穷和 C(99) 向 0 四舍五入,但在两种语言中 (n/m)*m + n%m == n,因此 % 运算符必须在正确的方向上进行补偿。

    Ada 更明确,并且兼具modrem

    【讨论】:

    • @Svante,在库中很容易实现。
    • @Pacerier:是的,这是一种绿色纺纱形式。取模和取余也是非常低级的运算,因此从高级库确保效率并非易事。
    • @Svante,Greenspunning 是生活的一部分。它甚至存在于 Common Lisp 中,只是 color 不再是绿色。
    • Python 模不完全是“真”模,因为它允许 M
    【解决方案3】:

    在 C89/90 中,带有负操作数的除法运算符和余数运算符的行为是实现定义的,这意味着根据实现,您可以获得任何一种行为。只需要运算符彼此同意:从a / b = qa % b = r 遵循a = b * q + r。如果行为严重依赖结果,请在代码中使用静态断言来检查行为。

    在 C99 中,您观察到的行为已成为标准。

    事实上,任何一种行为都有一定的逻辑。 Python 的行为实现了真正的模运算。您观察到的行为是 C 与向 0 舍入一致(这也是 Fortran 行为)。

    在 C 中倾向于向 0 舍入的原因之一是很自然地期望 -a / b 的结果与 -(a / b) 相同。在真正的模数行为的情况下,-1 % 10 将评估为 9,这意味着 -1 / 10 必须为 -1。这可能被视为相当不自然,因为 -(1 / 10) 为 0。

    【讨论】:

    • 整数除法是周期性的; (a+kb)/b = (a/b)+k。实数除法是周期性的和对称的。尝试定义整数除法以增加对称性将使其不再是周期性的,除非除数是奇数并且使用舍入到最近的语义计算。我认为周期性比对称性更重要,尽管其他人可能会有所不同。
    【解决方案4】:

    两个答案都是正确的,因为-1 modulo 109 modulo 10 相同。

    r = (a mod m)
    a = n*q + r
    

    您可以确定|r| < |n|,但不能确定r 的值是多少。有2个答案,否定和肯定。


    在 C89 中,虽然答案总是正确的,但模运算的确切值(他们将其称为余数)是未定义的,这意味着它可以是负结果或正结果。在 C99 中定义了结果。

    如果你想要肯定的答案,如果你发现你的答案是否定的,你可以简单地加 10。

    要让模运算符在所有语言上都一样,请记住:

    n mod M == (n + M) mod M
    

    一般来说:

    n mod M == (n + X * M) mod M
    

    【讨论】:

    • C 中负数的模定义:通过以下语句: 如果商a/b 是可表示的,则表达式(a/b)*b + a%b 应等于@987654331 @.
    • (我应该补充一点,在 C 中 % 实际上并未定义为“模”运算符 - 它被定义为“余数”)。
    • 看来是这样,我正在浏览这个维基百科页面,该页面声明它没有在 C89 中定义:en.wikipedia.org/wiki/Modulo_operation
    • 其实现已定义,即它的 -1 或 9 但必须是其中之一
    • @Brian / jk:答案不正确。 C 的当前标准是 C99,而不是 C89,并且 C99 确实很好地定义了 remainder 运算符,正如 Brian 链接的维基百科网站确实清楚地说明了。
    【解决方案5】:

    执行欧几里得除法a = b*q + r,就像将分数a/b四舍五入为整数商q,然后计算余数r

    您看到的不同结果取决于用于舍入商的约定...

    如果你向零舍入(截断),你会得到一个围绕零的对称性,就像在 C 中一样:

    truncate(7/3) = 2
    7 = 3*2 + 1
    
    truncate(-7/3) = -2
    -7 = 3* -2 - 1
    
    truncate(7/-3) = -2
    7 = -3* -2 + 1
    

    如果你向负无穷大(下限)取整,你会得到一个类似于 Python 的余数:

    floor(7/3) = 2
    7 = 3*2 + 1
    
    floor(-7/3) = -3
    -7 = 3* -3 + 2
    
    floor(7/-3) = -3
    7 = -3* -3 - 2
    

    如果您四舍五入到最接近的 int(与您想要的任何值、偶数或远离零的值相关联),您将得到一个居中的模数:

    round(7/3) = 2
    7 = 3*2 + 1
    
    round(8/3) = 3
    8 = 3*3 - 1
    
    round(-7/3) = -2
    -7 = 3* -2 - 1
    
    round(7/-3) = -2
    7 = -3* -2 + 1
    

    您可以尝试实现自己的模数,并朝正无穷大 (ceil) 四舍五入,并且您会发明一个相当非传统的模数,但它仍然是一种模数...

    【讨论】:

      【解决方案6】:

      从 python 3.7 开始,您还可以使用来自math 内置模块的.remainder()

      Python 3.7.0a0 (heads/master:f34c685020, May  8 2017, 15:35:30)
      [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
      Type "help", "copyright", "credits" or "license" for more information.
      >>> import math
      >>> math.remainder(-1, 10)
      -1.0
      

      来自docs

      返回 x 相对于 y 的 IEEE 754 样式余数。对于有限 x 和有限非零 y,这是 x - n*y 的差值,其中 n 是与商 x / y 的精确值最接近的整数。如果x / y 恰好在两个连续整数之间,则最接近的偶数将用于n。因此,余数 r = remainder(x, y) 总是满足 abs(r) <= 0.5 * abs(y)

      特殊情况遵循 IEEE 754:特别是,remainder(x, math.inf) 是任何有限 x 的 x,remainder(x, 0)remainder(math.inf, x) 引发任何非 NaN x 的 ValueError。如果余数运算的结果为零,则该零的符号与 x 相同。

      在使用 IEEE 754 二进制浮点的平台上,此操作的结果始终可以精确表示:不会引入舍入误差。

      【讨论】:

      • 这不能回答问题的任何一部分。
      • @Yola 你在技术上是正确的,但这个答案增加了线程的价值。我还认为,仅仅询问How to make mod operation in C to be the same like in Python? 是不必要的片面,实际上也应该包括How to make Python behave like C
      • 我其实是在找这个。谢谢。
      猜你喜欢
      • 1970-01-01
      • 2014-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-29
      • 2018-04-02
      • 2015-12-16
      相关资源
      最近更新 更多