本文介绍几种常见的整除取整方式,以及 % 运算问题。
一. 取整方式
以C语言为例,相信大多数同学都知道以下事实:
int a = 5, b = 2;
a / b; // 答案为2,不是2.5
当 a, b都是整形变量时,相除的结果必定也是整形,所以 a / b 不会是 2.5,那么为什么结果是2,而不是3呢?
int a = 5, b = -2;
a / b; // 答案为-2,不是-2.5
同样的,为什么答案是 -2 ,而不是 -3 呢?
要解决这个问题,我们首先要了解一下常见的几种取整方式。
1. 0向取整
int main()
{
//本质是向0取整
int i = -2.9;
int j = 2.9;
printf("%d\n", i); //结果是:-2
printf("%d\n", j); //结果是:2
return 0;
}
这种取整方式是C语言在整除或者整形截断的时候默认采取的取整方式,它的做法是将取整后的结果尽可能的靠近 0 。
如 -2.9 取整后变成 -2 而非 -3 是因为 -2 比 -3 更加靠近 0。
从数值上看,这种取整方式就是直接抹去浮点数的小数位。
再次强调,向0取整是C语言默认的取整方式。
2.向 -∞ 取整
#include <stdio.h>
#include <math.h>
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;
}
这种取整方式就是让取整后的结果尽可能的小,又叫地板取整,C语言math库中 floor() 函数可以实现这种取整方式。
3.向 +∞ 取整
#include <stdio.h>
#include <math.h>
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;
}
这种取整方式就是让取整后的结果尽可能的大,即靠近正无穷,C语言math库中 ceil() 函数可以实现这种取整方式。
4. 四舍五入取整
#include <stdio.h>
#include <math.h>
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;
}
这种就是最为常见的四舍五入,同样的,math 库中的 round()函数可以实现。
下面给出一个汇总例子,可以帮助大家理解:
#include <stdio.h>
#include <math.h>
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;
}
注:trunc()函数也可实现向0取整。
二. % 运算
1.概念
如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = q*d + r , q 为整数,且0 ≤ |r| < |d|。其中,q 被称为商,r 被称为余数。
此概念非常重要,一些特殊的取模/余情况都要依据此概念才能得到合理的解释。
看例子
(1) 10 % 3 == 1
解释:10 = 3 * 3 + 1,故 3 是商,1 是余数。
(2) -10 % 3
C语言:-1
解释:-10 = 3 * (-3) + (-1) 故 -3 是商,-1是余数。
Python: 2
解释:-10 = 3 * (-4) + 2 故-4是商,2是余数。
对于这个例子,我们可以看到,C和Python的解释都符合我们的定义,那么到底是什么形成了这种差异呢?
2.取模和取余的差别
取模和取余大多数情况下是一样的,可是在某些特殊的情况下还是有些差别的。
本质:
取余:尽可能让商,进行向0取整。
取模:尽可能让商,向-∞方向取整。
本质差别就是,商的取整方式不同!!
由上述 -10 % 3 例子,我们可以看到 C 默认取整方式是向0取整,而Python默认向负无穷取整,而根据定义,才导致 -10 % 3在C和Python中结果不同。
我们也可以这么说:
C中 % ,本质其实是取余。
Python中 % ,本质其实是取模。
请注意,无论是C的向0取整,还是Python的向负无穷取整,在除数和被除数符号相同(即商是正数)的情况下都是一样的,即此时,取模等价于取余!,所以我们平时使用并未感觉到差异,因为除数和被除数符号不同的情况的确很少见。
3.总结
上面讲了这么多,那么假如我们遇到了一些特殊情况,如何计算 % 的结果呢?
1.求出商(小数)
第一步,我们首先不考虑取整的情况,直接先计算出商,如 10 / (-3) == -3.333333
2.考虑如何取整
第二步,根据语言特性对所得商进行取整,如在C语言中采用向0取整,即 -3.333333取整后为-3。
注:如果不清楚语言取整方式,可以进行一下简单的测试。
3.根据公式 a = q*d + r 计算出 % 的结果
第三步,套公式。如10%(-3),我们在第二步已经得出 q = 10 / (-3) = -3。
由 10 = (-3)*(-3) + r 得出 r = 1,故 C语言中 10 % (-3) == 1。大家可依照此方法自行退到一下Pyhton中 10 % (-3) 的值。
其实说了这么多,总的来说就是一句话,对于模除余运算,取整方式绝对一切! ,一般语言的默认取整方式基本上都是向0取整或向负无穷取整。只要我们将该语言默认取整方式搞清楚,在根据公式,我们就可以轻易得出 % 运算的结果。
笔者水平有限,若有大佬指出错误或不足之处,笔者感激不尽!