大家在日常编程或者学习中都会经常接触到取余/取模运算,那么计算机中取余/取模运算究竟是怎么定义的,读完本篇文章,相信你一点会有很大收获!
- step 1:你可能不知道的四种“取整”方式
- 1.1 C语言中本质是:向0取整
- 1.2 `floor` 函数:“地板取整”,即统一向小的方向取整。
- 1.3 `ceil` 函数:统一向大的方向取整。
- 1.4 `round` 函数:熟知的四舍五入取整
- 1.5 汇总例子
- step 2:用公式理解“取模”
- 2.1 取模概念
- 2.2 对“取模”修订版的定义
- 2.3 结论
- step 3:“取余”和“取模”一样吗?
- 3.1 本质1: 取整
- 3.2 本质2: 符号
- step 4:如果参与运算的数据是不同符号的
- step 5:总结
首先谈谈关于数学取整的问题
1.1 C语言中本质是:向0取整#include
#include
int main()
{
printf("%d\n", 2.9); //结果是:2
printf("%d\n", -2.9); //结果是:-2
//trunc取整函数,同作用。
printf("%d\n", (int)turnc(2.9)); //结果是:2
printf("%d\n", (int)trunc(-2.9)); //结果是:-2
return 0;
}
1.2 floor
函数:“地板取整”,即统一向小的方向取整。
#include
#include //因为使用了floor函数,需要添加该头文件
int main()
{
//本质是向-∞取整,注意输出格式要不然看不到结果
printf("%.1f\n", floor(-2.9)); //-3.0
printf("%.1f\n", floor(-2.1)); //-3.0
printf("%.1f\n", floor(2.9)); //2.0
printf("%.1f\n", floor(2.1)); //2.0
return 0;
}
1.3 ceil
函数:统一向大的方向取整。
#include
#include
int main()
{
//本质是向+∞取整,注意输出格式要不然看不到结果
printf("%.1f\n", ceil(-2.9)); //-2.0
printf("%.1f\n", ceil(-2.1)); //-2.0
printf("%.1f\n", ceil(2.9)); //3.0
printf("%.1f\n", ceil(2.1)); //3.0
return 0;
}
1.4 round
函数:熟知的四舍五入取整
#include
#include
int main()
{
//本质是四舍五入
printf("%.1f\n", round(2.1)); //2.0
printf("%.1f\n", round(2.9)); //3.0
printf("%.1f\n", round(-2.1)); //-2.0
printf("%.1f\n", round(-2.9)); //-3.0
return 0;
}
1.5 汇总例子
#include
#include
int main()
{
const char * format = "%.1f \t%.1f \t%.1f \t%.1f \t%.1f\n";
printf("value\tround\tfloor\tceil\ttrunc\n");
printf("-----\t-----\t-----\t----\t-----\n");
printf(format, 2.3, round(2.3), floor(2.3), ceil(2.3), trunc(2.3));
printf(format, 3.8, round(3.8), floor(3.8), ceil(3.8), trunc(3.8));
printf(format, 5.5, round(5.5), floor(5.5), ceil(5.5), trunc(5.5));
printf(format, -2.3, round(-2.3), floor(-2.3), ceil(-2.3), trunc(-2.3));
printf(format, -3.8, round(-3.8), floor(-3.8), ceil(-3.8), trunc(-3.8));
printf(format, -5.5, round(-5.5), floor(-5.5), ceil(-5.5), trunc(-5.5));
return 0;
}
结论:浮点数(整数/整数),是有很多的取整方式的。具体使用哪种方式,取决于具体情况。
step 2:用公式理解“取模” 2.1 取模概念如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = q*d + r 且 0 ≤ r < d
。其中,q 被称为商,r 被称为余数。
//对应代码
#include
int main()
{
int a = 10;
int d = 3;
printf("%d\n", a%d); //结果是1
return 0;
}
因为:
a=10,d=3,q=3,r=1 0<=r
所以:
a = q*d+r -> 10=3*3+1
如果是下面的代码呢?
#include
int main()
{
int a = -10;
int d = 3;
//printf("%d\n", a/d); //C语言中是-3,很好理解
printf("%d\n", a%d); //-1
return 0;
}
- gcc 编译打印 -1
- 在
python
中,print(-10%3)
会打印 2
结论:很显然,上面关于取模的定义,并不能满足语言上的取模运算
因为在C中,现在 -10%3
出现了负数,根据定义:满足 a = q*d + r 且0 ≤ r < 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)
解释 :Python:-10 = (?)* 3 + 2
,其中,可以推到出来,'?'
必须是 -4 。即 -10 = (-4)* 3 + 2
,才能满足定义。
所以,在不同语言,同一个计算表达式,负数“取模”结果是不同的。我们可以称之为分别叫做正余数 和 负余数,上面的(-1)就是负余数,python里的2就是正余数。
2.3 结论由上面的例子可以看出,具体余数 r 的大小,本质是取决于商 q 的。 总结来说就是余数为多少取决于商为多少。
而商取决于除法计算的时候,所使用的取整规则。
C : -10/3 -> -3 -> 商向0取整
python: -10 -> -4 -> 商向-∞取整
大家应该看到了,上面的取模都是带着""
的。说明这两个并不能严格等价(虽然大部分情况差不多)
取余或者取模,都应该要算出商,然后才能得出余数。
3.1 本质1: 取整取余:尽可能让商,进行向0取整。
取模:尽可能让商,向-∞方向取整。
故:
C中 % ,本质其实是取余。
Python中 % ,本质其实是取模。
理解链:
对任何一个大于0的数,对其进行0向取整和-∞取整,取整方向是一致的。故取模等价于取余
对任何一个小于0的数,对其进行0向取整和-∞取整,取整方向是相反的。故取模不等价于取余
同符号数据相除,得到的商,一定是正数(正数vs正整数),即大于0!故,在对其商进行取整的时候,取模等价于取余。
3.2 本质2: 符号参与取余的两个数据,如果同符号,取模等价于取余
//计算数据同符号
#include
int main()
{
printf("%d\n", 10/3);
printf("%d\n\n", 10 % 3);
printf("%d\n", -10 / -3);
printf("%d\n\n", -10 % -3);
return 0;
}
Python 3.7
>>> print(10//3)
3
>>> print(10%3)
1
>>> print(-10//-3)
3
>>> print(-10%-3)
-1
注意:python中
/
默认是浮点数除法,//
才是整数除法,并进行-∞
取整
结论:通过对比试验,更加验证了,参与取余的两个数据,如果同符号,取模等价于取余
step 4:如果参与运算的数据是不同符号的#include
int main()
{
//a = q*d + r
printf("%d\n", -10 / 3); //结果:-3 向0取整
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
return 0;
}
明显结论:如果不同符号,余数(r)的求法,参考之前定义,确定商后决定余数。而余数符号,与被除数相同
这是真的吗?
>>> 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|
。其中,q 被称为商,r 被称为余数。
a = q*d + r
变换成 r = a - q*d
变换成 r = a + (-q*d)
对于:x = y + z,这样的表达式我们可以这么理解:x的符号 与 |y|、|z|中大的数据一致
而r = a + (-q*d)
中,|a| 和 |-q*d|的绝对值谁大,取决于商 q 的取整方式。
c是向0取整的,也就是 q 本身的绝对值是减小的。
如:
-10/3=-3.333.33
向0取整 -3. a=-10 |10|
, -q*d=-(-3)*3=9 |9|
10/-3=-3.333.33
向0取整 -3. a=10 |10|
, -q*d=-(-3)*(-3)=-9 |9|
绝对值都变小了
python是向-∞取整的,也就是q本身的绝对值是增大的。
-10/3=-3.333.33 '//'
向-∞取整 -4. a=-10 |10|, -q*d=-(-4)*3=12 |12|
10/-3=--3.333.33 '//'
向-∞取整 -4. a=10 |10|, -q*d=-(-4)*(-3)=-12 |12|
绝对值都变大了
结论:如果参与取余的两个数据符号不同,在C语言中(或者其他采用向0取整的语言如:C++,Java),余数符号,与被除数相同。
step 5:总结- 浮点数(或者整数相除),是有很多的取整方式的,0向取整、(正、负)无穷向取整、四舍五入取整…。(step 1证明)
- 如果 a 和 d 是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足
a = q*d + r
, q 为整数,且0 ≤ |r|< |d|
。其中,q 被称为商,r 被称为余数。(step 2证明) - 在不同语言,同一个计算表达式,“取模”结果是不同的。我们可以称之为分别叫做正余数 和 负余数(step 2证明)
- 具体余数 r 的大小,本质是取决于商 q 的。而商,又取决于除法计算的时候,取整规则。(step 2证明)
- 取余 vs 取模:取余是尽可能让商,进行向 0 取整。取模是尽可能让商,向
-∞
方向取整。(step 3证明) - 参与取余的两个数据,如果同符号,取模等价于取余(step 3证明)
- 如果参与取余的两个数据符号不同,在C语言中(或者其他采用向0取整的语言如:C++,Java),余数符号,与被除数相同。(因为采用的向0取整)(step 4证明)
以上就是关于计算机”取模/取余“的全部内容,相信大家看到这里应该也对取模/取余有了新的看法,很高兴能够为大家带来帮助,码字不易,如果大家觉得我这篇文章有帮助的话,可以👍支持一下。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)