取模 与 取整,多语言双平台详解取模方向,含推导过程

前言

主要参考 《C语言深度剖析(第二版)》整理,结合个人感悟,编译测试平台为 win10下VS2019和Linux平台,所有代码均经过本人测试没有问题。

取模

先看代码:

int a = -10;
int b = 3;
printf("%d\n", a / b);	//-3
printf("%d\n", a % b);	//-1

在 C/C++ 中进行运行时,答案就是这样,但是如果是在其他语言下,比如 python就会得到完全不一样的效果了,例如:在Python3.7.3中, print(-10%3) 得到的结果就是 2,所以我们要对取模运算的定义进行修正:

因为在C中,现在 -10%3 出现了负数,根据定义:满足 a = q ∗ d + r a = q*d + r a=qd+r 0 ≤ r < d 0 ≤ r < d 0r<d,C语言中的余数,是不满足定义的,因为:r<0了。

所以将 取模 的定义修订为: 如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = q*d + r , q 为整数,且 0 ≤ |r| < |d|。其中,q 被称为商,r 被称为余数。

有了这个新的定义,那么C中或者Python中的“取模”,就都能解释了。
解释C: − 10 = ( − 3 ) ∗ 3 + ( − 1 ) -10 = (-3) * 3 + (-1) 10=(3)3+(1)
解释Python: − 10 = ( ) ∗ 3 + 2 -10 = ( )* 3 + 2 10=3+2,其中,可以推导出来, ? ? ?必须是-4(后面验证).即 − 10 = ( − 4 ) ∗ 3 + 2 -10 = (-4)* 3 + 2 10=43+2,才能满足定义。
所以,在不同语言,同一个计算表达式,负数“取模”结果是不同的。我们可以称之为分别叫做 正余数负余数
正是因为 负余数 和 正余数 的区别,才得到了不同的取余结果

注意:在 C/C++, C#, JAVA, PHP这几门主流语言中, % \% % 运算符都是做取余运算,而在 python 中的 % \% % 是做 取模 运算。

由上面的例子可以看出,具体余数r的大小,本质是取决于 商q 的。而商,又取决谁呢?取决于除法计算的时候,取整规则,就是因为取整规则的不同,导致了取余结果的不同

取余与取模的不同

取余、取模 在计算时都要先计算出 商,然后得到余数,本质上有以下区别:

  • 取整方式
    取余:尽可能让商,进行向0取整
    取模:尽可能让商,进行向 − ∞ -\infty 取整

    所以在C 语言中,取模的本质是 取余,在Python中,取模的本质是 取模

    在本例中,由于两个数字符号不同,向0 和 向 − ∞ -\infty 取整的方向是不同的,但如果都是正数,则 取整方向是一样的

  • 符号
    参与 取余 的两个数据,如果同符号,取模 等价于 取余

总之,如果两个数字符号一样,那么 取余 和 取模 是一样的,如果符号不同,则有区别

不同语言对于取模结果的差异

在C 语言中,有如下结果:

printf("%d\n", -10 / 3); 	//结果:-3
printf("%d\n\n", -10 % 3); 	//结果:-1 为什么? -10=(-3)*3+(-1)
printf("%d\n", 10 / -3); 	//结果:-3
printf("%d\n\n", 10 % -3); 	//结果:1 为什么?10=(-3)*(-3)+1

似乎可以推断出 取模的结果(余数) 的符号,与被除数相同 [被除数 / 除数 = 商 …… 余数]

在 Python中,有如下结果:

>>> print(-10/3)
-4

>>> print(10/-3)
-4

>>> print(-10%3)
2 

>>> print(10%-3)
-2

这里看Python又与上面的推断完全相反

数学推论

这里进行简单的数学推导,理解这个过程:

我们不怎么严谨的数学推导,理解一下即可:
重新看看定义:
如果 a(被除数) 和 d(除数) 是两个自然数,d非零,可以证明存在两个 唯一 的整数 q 和 r,满足 a = q*d + r , q 为整数,且 0 ≤ ∣ r ∣ < ∣ d ∣ 0 ≤ |r| < |d| 0r<d。其中,q 被称为商,r 被称为余数

a = q ∗ d + r a = q*d + r a=qd+r 变换成 r = a − q ∗ d r = a - q*d r=aqd 变换成 r = a + ( − q ∗ d ) r = a + (-q*d) r=a+(qd)
对于: x = y + z x = y + z x=y+z,这样的表达式,x 的符号 与 ∣ y ∣ |y| y ∣ z ∣ |z| z 中大的数据一致(这里没有给出严谨的定义,只是用了主观的结论)
r = a + ( − q ∗ d ) r = a + (-q*d) r=a+(qd) 中, ∣ a ∣ |a| a ∣ − q ∗ d ∣ |-q*d| qd 的绝对值谁大,取决于 商q 的取整方式。
c是向0取整的,也就是q本身的 绝对值 是减小的。
如:
− 10 / 3 = − 3.333.33 -10/3=-3.333.33 10/3=3.333.33 向0取整 -3. a = − 10 a=-10 a=10 -> ∣ a ∣ = ∣ 10 ∣ |a|=|10| a=10, 得: − q ∗ d = − ( − 3 ) ∗ 3 = 9 -q*d=-(-3)*3=9 qd=(3)3=9 即:|9|
10 / − 3 = − 3.333.33 10/-3=-3.333.33 10/3=3.333.33 向0取整 -3. a = 10 a=10 a=10 -> ∣ a ∣ = ∣ 10 ∣ |a|=|10| a=10, 得: − q ∗ d = − ( − 3 ) ∗ ( − 3 ) = − 9 -q*d=-(-3)*(-3)=-9 qd=(3)(3)=9 即:|9|
绝对值都变小了

python是向 − ∞ -\infty 取整的,也就是q本身的绝对值是增大的。
− 10 / 3 = − 3.333.33 -10/3=-3.333.33 10/3=3.333.33 '//'向 − ∞ -\infty 取整 -4. a = − 10 a=-10 a=10 -> ∣ a ∣ = ∣ 10 ∣ |a|=|10| a=10, − q ∗ d = − ( − 4 ) ∗ 3 = 12 -q*d=-(-4)*3=12 qd=(4)3=12 即:|12|
10 / − 3 = − − 3.333.33 10/-3=--3.333.33 10/3=3.333.33 '//'向 − ∞ -\infty 取整 -4. a = 10 a=10 a=10 -> ∣ a ∣ = ∣ 10 ∣ |a|=|10| a=10, − q ∗ d = − ( − 4 ) ∗ ( − 3 ) = − 12 -q*d=-(-4)*(-3)=-12 qd=(4)(3)=12 即:|12|
绝对值都变大了

结论:如果参与取余的两个数据符号不同,在C语言中 (或者其他采用向0取整的语言如:C++, Java, php),余数符号,与被除数相同。

总结

  • 浮点数(或者整数相除),是有很多的取整方式的。
  • 如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = q ∗ d + r a = q*d + r a=qd+r , q 为整数,且 0 ≤ ∣ r ∣ < ∣ d ∣ 0 ≤ |r|< |d| 0r<d。其中,q 被称为商,r 被称为余数。(a为被除数,b为除数)
  • 在不同语言,同一个计算表达式,“取模”结果是不同的。我们可以称之为分别叫做 正余数负余数 具体 余数r 的大小,本质是取决于 商q 的。而 ,又取决于除法计算的时候,取整规则
  • 取余vs取模: 取余 尽可能让商,进行向0取整。取模尽可能让商,向 − ∞ -\infty 方向取整。
  • 参与取余的两个数据,如果同符号,取模等价于取余
  • 如果参与取余的两个数据符号不同,在C语言中(或者其他采用向0取整的语言如:C++,Java),余数符号,与被
    除数相同。(因为采用的向0取整)
printf("%d\n", 2 / (-2)); 	//-1
printf("%d\n", 2 % (-2)); 	//0 因为:2=(-1)*(-2)+r
最后

感谢观赏,一起提高,慢慢变强

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值