C,Python – 模(%)操作的不同行为
我发现,根据使用的语言,相同的mod操作会产生不同的结果。
在Python中:
-1 % 10
产生9
在C它产生-1 !
1)哪一个是正确的模?
2)如何使C中的mod操作与Python中一样?
- 两种变体都是正确的,但是在math(尤其是数论)中,Python的模是最常用的。
- 在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更加明确,并且具有mod
和rem
两者。
在C89 / 90中,除法操作符和余数操作符的操作数为负的行为是实现定义的 ,这意味着取决于实现,您可以获得任一行为。 只需要操作员彼此一致:从a / b = q
和a % 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 10
与9 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二进制浮点的平台上,此操作的结果始终可以精确表示:不引入舍入错误。