C的除法往往也会隐藏着诸多陷阱,例如我们常常碰到的整数除,但是今天谈到的是关于取余运算。
假定我们让a除以b,商为q,余数为r:
q = a / b ;
r = a % b ;
在这里我们不妨假定b大于0。
我们希望a、b、q、r之间维持怎么样的关系?
1. 最重要的一点,q*b+r = a要成立,余数的定义嘛
2. 如果我们改变a的正负号,我们希望这会改变q的符号,但这不会改变q的绝对值。
3. 当b>0时,我们希望保证r>=0且r<b。例如,如果余数用于哈希表的索引,确保它是一个有效地索引值很重要。
上述三条性质是不是都得到满足呢?稍微举一个例子我们就能发现这个不可能同时成立。比如3/2,商为1,余数为1.此时,第一条性质得到满足。(-3)/2如果满足第2条性质的话,答案应该是商为-1,但是这样余数也为-1,这样性质3就不成立了。如果我们优先满足性质3,即余数为1,这种情况下商为-2,那么性质2不成立。
总结:C语言在实现证书除法截断运算时,必须放弃上述三条原则中的一条。大多数程序设计语言选择放弃第3条,而改为要求余数与被除数的正负号相同。
下面测试一下Visual Studio 2008编译器
#include <stdio.h> int main() { int a = -13; int b = 3; int c = a/b; int d = a%b; printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d); return 0; }
根据性质1和性质2,同时性质3(余数的符号与被除数的符号一致),我们可以猜测出商为-4,余数为-1.
执行结果为:
我自己也总结了一下:根据性质2,我们可以在被除数与除数异号的情形下,将它们转化为绝对值进行计算,最后根据性质3,确定商与余数的符号。毕竟商的绝对值是不变的。例如,上例中可以计算13/3 = 4, 13%3 = 1,根据性质2与性质3,(-13)/3 = -4, (-13)%3 = -1。