负数如何取模?深度理解取模:取余运算


在了解取模和取余运算之前,我们必须先了解 取整,因为余数是先确定好整数商之后的附带产物,而取模和取余的概念又非常相似(没错,取模和取余是不同的)

关于”取整“,你必须得知道的事儿

C语言采取的取整规则

#include <stdio.h>
int main() {

    int a = 2.9;
    int b = -2.9;
    printf("%d\n", a);
    printf("%d\n", b);
    return 0;
}

image-20220831204038140

结果是2和-2,大家应该是知道的,但是大家了解原理吗?看下面的图

image-20220831211055018

C语言采取的就是上图所述的向0取整

取整函数trunc

取整函数trunc采用的也是向0取整规则

#include <stdio.h>
#include <math.h>
int main() {
    printf("%.1f\n", trunc(-2.1));
    printf("%.1f\n", trunc(-2.9));
    printf("%.1f\n", trunc(2.1));
    printf("%.1f\n", trunc(2.9));
    return 0;
}

image-20220831204656669

取整函数ceil

#include <stdio.h>
#include <math.h>
int main() {
    printf("%.1f\n", ceil(-2.1));
    printf("%.1f\n", ceil(-2.9));
    printf("%.1f\n", ceil(2.1));
    printf("%.1f\n", ceil(2.9));
    return 0;
}

image-20220831204931618

结果好像都变大了

image-20220831211125147

那是因为ceil函数取整采用的规则是向+∞取整

取整函数floor

#include <stdio.h>
#include <math.h>
int main() {
    printf("%.1f\n", floor(-2.1));
    printf("%.1f\n", floor(-2.9));
    printf("%.1f\n", floor(2.1));
    printf("%.1f\n", floor(2.9));
    return 0;
}

image-20220831205335393

结果又全都变小了

image-20220831211137483

因为floor函数取整采取的是向-∞取整

取整函数round

#include <stdio.h>
#include <math.h>
int main() {
    printf("%.1f\n", round(-2.1));
    printf("%.1f\n", round(-2.9));
    printf("%.1f\n", round(2.1));
    printf("%.1f\n", round(2.9));
    return 0;
}

image-20220831205157901

这结果有点像四舍五入

image-20220831211148859

没错,round函数取整采取的是四舍五入取整

结论

浮点数(整数/整数),其实是有很多种取整方式的(四种),而C语言采取的是向0取整

是时候该聊聊取模和取余了

先看一下取模的定义:

如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数q和r,满足a = q * d + r 且0 <= r < d。其中q被称为商,r被称为余数。

看下面代码:

#include <stdio.h>
int main()
{
    int a = 10;
    int b = 3;
    printf("%d\n", a / b);
    printf("%d\n", a % b);
    return 0;
}

image-20220831215516403

可以看到结果分别是3和1,此时还没有任何的问题。

#include <stdio.h>
int main()
{
    int a = -10;
    int b = 3;
    printf("%d\n", a / b);
    printf("%d\n", a % b);
	return 0;
}

image-20220831215714181

再看结果,分别是-3和-1,这里有一点不对劲儿了,-3还好理解,但是-1好像跟定义不太符合,再继续往下看

image-20220831220949016

在python中,正数的“取模”是1非常正确,但是负数的“取模”竟然是2,这好像太不对劲儿了吧,但是这个2是符合定义的。

从这里可以得出一点结论:关于取模的定义,并不能满足语言上的取模运算

因为在C语言中,余数出现了负数,这显然是不符合定义的,所以现在这里有了一个修订版的定义:

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

有了这个新的定义,那么C中和python中的“取模”,就都能解释了。

解释C:-10 = (-3) * 3 + (-1)

解释python:-10 = (-4) * 3 + 2

那么是什么造成了C和python这种现象的呢?

我们一定要知道一点,余数一定是根据商来求得的,余数的变化随商的变化而变化。

那么上面C和python余数的不同肯定是由于商的不同而造成的,上图中也可以看出,C中负数的商是-3而python负数的商却是-4,那么这是为什么呢?

因为C和python遵守的取整规则不同,C遵守的是向0取整,而python遵守的是向-∞取整

取余和取模一样吗?

取余和取模并不能严格等价(虽然大部分情况差不多)

先看取整

取余:尽可能让商进行向0取整

取模:尽可能让商进行向-∞取整

所以

C中%,本质是取余

python中%,本质才是取模

理解链:

对任何一个大于0的数,对其进行向0取整或者-∞取整,取整方向是一致的。故取模等价于取余

对任何一个小于0的数,对其进行向0取整或者-∞取整,取整方向是相反的。故取模不等价于取余

而同符号数据得到的商一定是正数,所以此时取模也等价于取余。

image-20220831225229898

image-20220831225440361

通过对比实验,更加验证了,参与取余的两个数据,如果同符号,取模等价于取余。

如果参与运算的数据,不同符号呢?

#include <stdio.h>
int main()
{
    printf("%d\n", -10 / 3);
    printf("%d\n", 10 / -3);
    printf("%d\n", -10 % 3);
    printf("%d\n", 10 % -3);
    return 0;
}

image-20220906221750106

通过结论可以看到:如果不同符号,参考之前的定义,余数的符号是和被除数的符号相同的。

然而真的是这样吗?

image-20220906221908699

?????

这是怎么回事?

上面讲过了C语言中的求整规则是向0取整,而python中的取整规则是向负∞取整。

其实导致上图的二者之间的余数的符号的区别,也是由于C语言和python采取的取整规则不同。

-10 // 3 = -4 ,而-4*3=-12,所以余数是-10 - (-12) = 2,

10 // -3 = -4 ,而-4*-3=12,所以余数是10 - 12 = -2

结论:如果参与取余的两个数的符号不同,在采取向0取整的语言中(C语言、C++、Java),余数符号和被除数相同;而在采取向负∞取整的语言中(python),余数符号相对是无规则的,具体符号可根据公式倒退出来。

总结

  • 浮点数(或者整数相除),是有很多的取整方式的。
  • 如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数q和r,满足a = q * d + r,q为整数,且0 <= |r| < |d|。其中,q被称为商,r被称为余数。
  • 在不同语言中,同一个计算表达式,“取模”结果是不同的。
  • 具体余数r的大小,本质是取决于商q的。而商,又取决于除法计算的时候的取整规则。
  • 取余vs取模:取余尽可能让商进行向0取整。取模尽可能让商进行向-∞取整。
  • 参与取余的两个数据,如果同符号,取模等价于取余。
  • 如果参与取余的两个数的符号不同,在采取向0取整的语言中(C语言、C++、Java),余数符号和被除数相同;而在采取向负∞取整的语言中(python),余数符号相对是无规则的,具体符号可根据公式倒退出来。
  • 10
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云朵c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值