C 和 Python-模(%)操作的不同行为

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

在 Python 中:

-1 % 10

产生 9

在 C 中它产生 -1

  1. 哪一个是正确的模数?
  2. 如何使 C 中的 mod 操作与 Python 中的相同?
23053 次浏览

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

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

可以确定 |r| < |n|,但不能确定 r的值。有两个答案,消极的和积极的。


在 c89中,虽然答案总是正确的,但是模除的确切值(他们称之为余数)是未定义的,这意味着它可以是一个负结果,也可以是一个正结果。在 C99中定义了结果。

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

要让模运算符在所有语言上都能运行,只需记住:

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

一般来说:

n mod M == (n + X * M) mod M
  1. 这两种变体都是正确的,然而在数学(特别是数论)中,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 = ra = b * q + r。如果行为严重依赖于结果,则在代码中使用静态断言来检查行为。

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

事实上,这两种行为都有一定的逻辑。巨蟒的行为实现了真正的模除。您观察到的行为是 C 与舍入为0(也是 Fortran 行为)一致。

在 C 语言中向0舍入的原因之一是,期望 -a / b的结果与 -(a / b)相同是很自然的。对于真正的模行为,-1 % 10的值为9,这意味着 -1 / 10必须为 -1。由于 -(1 / 10)为0,这可能被认为是相当不自然的。

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

来自 医生:

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

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

在使用 IEEE 754二进制浮点运算的平台上,这种运算的结果始终是完全可表示的: 没有引入舍入误差。

执行带余除法 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

你可以尝试实现你自己的模四舍五入到正无穷大(细胞) ,你会 发明一个相当非常规的模,但它仍然是一种模..。