在早期的课程中,我被教导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.为什么?这在技术上是不是错了?
Yu *_*Hao 20
简答:
标准保证(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章给出了不同的答案:
对于负操作数,截断的方向为/和%的结果的符号与机器有关,在上溢或下溢时采取的操作也是如此.
根据这个,任何一个-1或10可以是合法的结果.
原因在于C89 3.3.5:
当整数被划分并且除法不精确时,如果两个操作数都是正数,则/运算符的结果是小于代数商的最大整数,并且%运算符的结果是正数.如果任一操作数为负,则/运算符的结果是小于代数商的最大整数还是大于代数商的最小整数是实现定义的,这是%运算符的结果的符号.如果商a/b是可表示的,则表达式(a/b)*b + a%b应等于a.
事实证明这是从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
Run Code Online (Sandbox Code Playgroud)