C语言与Python中对负数的%运算结果不同的原因

无论是C语言还是在Python中,取模运算,比如,6%5都得到1,一切是那么和谐。
如果被除数是负数,比如,C语言中,-6%5=-1, Python中,-6%5=4,问题来了,这是为什么? 还有6%(-5), -6%(-5), 它们在C语言和Python中的运算结果分别是什么?

引入问题

考虑被除数和除数中有一个或两个是负数,C语言和Python的%运算的运算结果分别是什么样的?会是一样的吗?为什么? 如果下面C语言代码和Python的代码的输出全部猜对,(可以只关心%运算的结果,不关心除法的),那么你好棒棒哦^_^,可以不用往下看了:
 
// C语言的
#include <stdio.h>
int main(int argc, char **argv)
{
    int a, b, c, d;
	a = 6; b = 5; c = a%b; d=a/b;
	printf("a=%d, b=%d, a%%b=%d, a/b=%d\n", a, b, c, d);
	a = 6; b = -5; c = a%b; d=a/b;
	printf("a=%d, b=%d, a%%b=%d, a/b=%d\n", a, b, c, d);
	a = -6; b = 5; c = a%b; d=a/b;
	printf("a=%d, b=%d, a%%b=%d, a/b=%d\n", a, b, c, d);
	a = -6; b = -5; c = a%b; d=a/b;
	printf("a=%d, b=%d, a%%b=%d, a/b=%d\n", a, b, c, d);
}
# Python的
#!/usr/bin/python3
a = 6; b = 5; c = a%b; d=int(a/b);
print("a=%d, b=%d, a%%b=%d, a/b=%d"%(a, b, c, d) );
a = 6; b = -5; c = a%b; d=int(a/b);
print("a=%d, b=%d, a%%b=%d, a/b=%d"%( a, b, c, d) );
a = -6; b = 5; c = a%b; d=int(a/b);
print("a=%d, b=%d, a%%b=%d, a/b=%d"%( a, b, c, d) );
a = -6; b = -5; c = a%b; d=int(a/b);
print("a=%d, b=%d, a%%b=%d, a/b=%d"%( a, b, c, d) );
实际输出是这样的:
//C语言的
a=6, b=5, a%b=1, a/b=1
a=6, b=-5, a%b=1, a/b=-1
a=-6, b=5, a%b=-1, a/b=-1
a=-6, b=-5, a%b=-1, a/b=1
# Python的
a=6, b=5, a%b=1, a/b=1
a=6, b=-5, a%b=-4, a/b=-1
a=-6, b=5, a%b=4, a/b=-1
a=-6, b=-5, a%b=-1, a/b=1

如果输出结果和你想的不一样,请继续往下看,其实第一次见到这样的结果我也搞不懂是怎么回事。

给我一个解释呗

其实wikipedia已经说的比较详细了: Modulo_operation(不知道wikipedia被土啬后有没有解除啊? 打不开链接也无所谓,影响不大), 只是不那么直接,我再加工加工,更容易理解。

C语言进行%运算的时候,采用的是truncated divison, Python在进行%运算的时候,采用的是floored division。truncated divison是把一个小数向0的方向进行截断取整, floored divison是把一个小数向负无穷的方向截断整。

还是例子比如实在。约定:trunc(a/b)表示对a和b进行truncated divison, floor(a/b)表示对a和b进行floored divison。 对于正数,两种方法没有区别。trunc(6/5)= floor(6/5) = 1。
对于负数,有区别了:trunc(-6/5)=-1, floor(-6/5) = -2。

约定:a表示被除数,n表示除数, r表示余数(r即我们的主角,%运算的结果, a, b, n为整数,b不等于0)。我们认为取模运算和%运算是一样的。 那么有:
C语言中的取模运算: r=a-n*trunc(a/n)
Python中的取模运算:r=a-n*floor(a/n)

我们可以用这两种不同规则解释上面程序的输出了。

对程序输出的解释

// C语言的
6%5 = 6-5*trunc(6/5) = 6-5 = 1
6%(-5) = 6-(-5)*trunc(6/(-5)) = 6+5*trunc(-1.2) = 1
-6%5 = -6-5*trunc(-6/5) = -6+5 = -1
-6%(-5) = -6-(-5)*trunc(-6/(-5)) = -6+5*trunc(1.2) =-1
#
6%5= 6-5*floor(6/5) = 1
6%(-5) = 6-(-5)*floor(6/(-5)) = 6+5*floor(-1.2) = 6-10 = -4
-6%5 = -6-5*floor(-6/5) = -6-5*floor(-1.2) = -6+10 = 4
-6%-5 = -6-(-5)*floor(-6/(-5)) = -6+5*floor(1.2) = -1

嗯,总算讲完了。应该够清晰了。