我发现相同的 mod 操作会根据使用的语言产生不同的结果。
在 Python 中:
-1 % 10
生产9
在 C 中它产生-1!
- 哪个是正确的模数?
- 如何使 C 中的 mod 操作与 Python 中的操作相同?
((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 更明确,同时具有 asmod
和rem
.
在 C89/90 中,带有负操作数的除法运算符和余数运算符的行为是实现定义的,这意味着根据实现,您可以获得任何一种行为。只需要运算符彼此同意: froma / b = q
和a % b = r
follow 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
是多少。有2个答案,否定和肯定。
在 C89 中,尽管答案总是正确的,但模运算的确切值(他们将其称为余数)是未定义的,这意味着它可以是负结果或正结果。在 C99 中定义了结果。
如果你想要肯定的答案,如果你发现你的答案是否定的,你可以简单地加 10。
要让模运算符在所有语言上都一样,请记住:
n mod M == (n + M) mod M
一般来说:
n mod M == (n + X * M) mod M
执行欧几里得除法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)四舍五入来实现自己的模数,并且您会发明一个相当非常规的模数,但它仍然是一种模数......
从 python 3.7 开始,您也可以使用.remainder()
内置math
模块。
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
来自文档:
返回 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,is x,对于任何非 NaN xremainder(x, 0)
,都会引发 ValueError。remainder(math.inf, x)
如果余数运算的结果为零,则该零的符号与 x 相同。在使用 IEEE 754 二进制浮点的平台上,此操作的结果始终可以精确表示:不引入舍入误差。