在之前的课程中,我学到了 n % d = r
的概念,并将其理解为 n = d*q + r
,其中 d
是除数,q
是商,r
是余数(请注意,余数永远不可能是负数)。
例如,-111 mod 11
的结果是 10
,因为 -111 = -11*-11 + 10
(与 -111 = -11*10 -1
不同,因为那样会得到一个负余数)。
然而,当打印 -111 % 11
的结果时,结果是 -1
而不是 10
。为什么?这难道不是技术上的错误吗?
简短回答:
标准保证了 (a/b)*b + a%b
等于 a
。
在 C99 中,除法运算符 /
的结果向零截断。在这种情况下,%
运算符的结果是确定的,为 -1
。
在 C89 中,对于负操作数,除法运算符 /
可能会以任一方式截断。因此,%
运算符的结果也取决于机器。
详细回答:
根据 C99 6.5.5,
5 / 运算符的结果是第一个操作数除以第二个操作数的商;% 运算符的结果是余数。在这两个操作中,如果第二个操作数的值为零,则其行为未定义。
6 当整数被除时,/ 运算符的结果是代数商,任何小数部分都被舍去。如果商 a/b 是可表示的,则表达式 (a/b)*b + a%b 必须等于 a;否则,a/b 和 a%b 的行为未定义。
同一页上的注释说明了 /
如何工作,它说:
这通常被称为“向零截断”。
根据这个规则,-111 / 11
只能是 -10
,而不是 1。由于 (a/b)*b + a%b
必须等于 a
,所以我们有 -111 % 11
等于 -1
。
然而,《The C Programming Language》第2.5章给出了不同的答案:
对于负操作数,/ 的截断方向和 % 的结果符号取决于机器,溢出或下溢时的行为也是如此。
根据这个规则,合法结果可能是 -1
或者 10
。
原因在于 C89 3.3.5 中:
当整数被除以无法整除时,如果两个操作数都是正的,则 / 运算符的结果是小于代数商的最大整数,% 运算符的结果是正的。如果任一操作数为负,则 / 运算符的结果是小于代数商的最大整数或大于代数商的最小整数是实现定义的,% 运算符的结果的符号也是如此。如果商 a/b 是可表示的,则表达式 (a/b)*b + a%b 必须等于 a。
这实际上是从 C89 到 C99 的变化。
C99 的解释提供了一些历史原因:
在 C89 中,涉及负操作数的整数除法可能会以实现定义的方式向上或向下取整;其目的是避免在运行时代码中产生特殊情况的开销并强制执行特定的行为。然而,在 Fortran 中,结果将始终向零截断,开销似乎可以被数字编程社区接受。因此,C99 现在要求类似的行为,这应该有助于从 Fortran 移植代码。本文档中 §7.20.6.
numer denom quot rem 7 3 2 1 –7 3 –2 –1 7 –3 –2 1 –7 –3 2 –1
-111 / 11
只能是-10,因此由于要求(a/b)*b + a%b
必须等于a
,所以-111 % 11
只能是-1。 - Adam Rosenfield&
和>>
通常被比作2的幂次数上的模数和除法,但是>>
并没有"朝零截断",因此-5 >> 1 == -3
,而-11&3 == 1
(而-11%4 == -3
)。 - i Code 4 Food对于模数,-1是错误的答案。
C语言的%
运算符是余数运算符而不是模数运算符,因此对于余数,10或-1都是可以接受的。
%
运算符的实现方式为,假设我们使用整数除法,那么 a == b * (a / b) + (a % b)
成立。
在这个例子中,-111 / 11
等于 -10
,所以有 -111 == 11 * -10 + x
,其中 x == -1
满足条件。
a % b = a - (a / b) * b
。由于-111 / 11
的结果为-10
,因此结果为-111 - (-10 * 11)
,即-1
。 - Rudy Velthuis
((a % b) + b) % b
- paddy