C,Python – 模(%)操作的不同行为

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

在Python中:

-1 % 10 

产生9

在C它产生-1

1)哪一个是正确的模?
2)如何使C中的mod操作与Python中一样?

  1. 两种变体都是正确的,但是在math(尤其是数论)中,Python的模是最常用的。
  2. 在C中,你可以((n % M) + M) % M得到与Python相同的结果。 例如, ((-1 % 10) + 10) % 10 。 请注意,对于正整数仍然有效: ((17 % 10) + 10) % 10 == 17 % 10 ,以及C实现的两个变体(正余数或负余数)。

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

它与如何处理负整数除法有直接的关系,即向0或减去无穷大。 Python向负无穷和C(99)向0逼近,但是在两种语言(n/m)*m + n%m == n ,所以%运算符必须以正确的方向进行补偿。

Ada更加明确,并且具有modrem两者。

在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。

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

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

你可以确定|r| < |n| |r| < |n| ,而不是r的价值。 有2个答案,消极和积极。


在C89中,虽然答案总是正确的,但模数运算的精确值(他们称之为余数)是不确定的,这意味着它可以是否定的结果,也可以是正的结果。 在C99中定义了结果。

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

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

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

一般来说:

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

由于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 

从文档 :

返回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二进制浮点的平台上,此操作的结果始终可以精确表示:不引入舍入错误。