在早期的课程中,我被教导n % d = r
并认为它是,除数n = d*q + r
在哪里,商在哪里,余数在哪里(注意余数永远不会是负数)。d
q
r
例如,-111 mod 11
is 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
is -1
。
然而,K&R 第 2.5 章给出了不同的答案:
/ 的截断方向和 % 的结果符号对于负操作数是机器相关的,上溢或下溢时采取的操作也是如此。
据此,-1
或者10
可以是合法的结果。
原因在 C89 3.3.5 中:
当整数被除且除法不精确时,如果两个操作数都是正数,则 / 运算符的结果是小于代数商的最大整数,而 % 运算符的结果是正数。如果任一操作数为负,则 / 运算符的结果是小于代数商的最大整数还是大于代数商的最小整数是实现定义的,% 运算符的结果的符号也是如此。如果商 a/b 是可表示的,则表达式 (a/b)*b + a%b 应等于 a 。
原来是从 C89 到 C99 的变化。
C99 Rationale 6.5.5 提供了一些历史原因:
在 C89 中,涉及负操作数的整数除法可以以实现定义的方式向上或向下舍入;目的是避免在运行时代码中产生开销以检查特殊情况并强制执行特定行为。然而,在 Fortran 中,结果总是会向零截断,而且开销似乎对数值编程社区来说是可以接受的。因此,C99 现在需要类似的行为,这应该有助于将代码从 Fortran 移植到 C。本文档第 7.20.6.2 节中的表格说明了所需的语义。
这是第 7.20.6.2 节中的表格:
numer denom quot rem
7 3 2 1
–7 3 –2 –1
7 –3 –2 1
–7 –3 2 –1
对于模数,-1 将是错误的答案。
尽管C 的%
运算符是余数运算符而不是模运算符 - 对于余数,10 或 -1 都是允许的。
运算符的%
实现a == b * (a / b) + (a % b)
是这样的,我们使用整数除法。
在这种情况下-111 / 11
是-10
,所以-111 == 11 * -10 + x
是满足的x == -1
。