C中的模运算有哪些规则?

在早期的课程中,我被教导n % d = r并将其视为n = d*q + r ,其中d是除数, q是商, r是余数(注意余数永远不会是消极的)。

所以例如, -111 mod 1110 ,因为-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和%b的行为都是未定义的。

并在同一页面上的脚注解释如何/工作,它说:

这通常被称为“截断为零”。

根据这个规则, -111 / 11只能是-10而不是1.由于(a/b)*b + a%b必须等于a ,我们有-111 % 11-1

然而,K&R第2.5章给出了不同的答案:

对于负操作数,%的截断方向和/的结果符号与机器有关,在上溢或下溢时采取的操作也是如此。

据此, -110可以是合法的结果。

原因在于C89 3.3.5:

当整数被划分并且除法不精确时,如果两个操作数都是正数,则/运算符的结果是小于代数商的最大整数,并且%运算符的结果是正数。 如果任一操作数为负,则/运算符的结果是小于代数商的最大整数还是大于代数商的最小整数是实现定义的,这是%运算符的结果的符号。 如果商a / b是可表示的,则表达式(a / b)* b + a%b应等于a。

事实certificate这是从C89到C99的变化。

C99理由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