深入解读取模和取余

/ 默认分类 / 0 条评论 / 1229浏览

取模和取余

一.引入

取模和取余操作相信大家都很熟悉,在很多场景我们都需要用到,比如数据分片,hash槽,随机请求流量控制等等。这篇博客将较为深入地总结一下这两种操作的异同点。

二.使用场景

三.取模和取余运算

先来看下向量的模,向量的模式数学术语,比如在二维空间,向量 AB(AB上面有→)的长度就叫做向量的模,记作|AB|(AB上有→). 模可以推广到更高的维度,其实就是向量的长度.这一点其实和计算机中的模也是有类似的含义。

取模和取余计算概念上有重叠的部分,只是在计算有负整数参与的运算的时候会有差异。其实就是记住一点。在取余的时候的时候,商的值需要向0的方向舍入,在取模的时候,商的值需要向负无穷的方向舍入。这里我引用百度百科的案例演示:

对于整型数a,b来说,取模运算或者求余运算的方法都是: 1.求整数商: c = [a/b]; 2.计算模或者余数: r = a - cb. 求模运算和求余运算在第一步不同: 取余运算在取c的值时,向0 方向舍入(fix()函数);而取模运算在计算c的值时,向负无穷方向舍入(floor()函数)。 例1.计算:-7 Mod 4 那么:a = -7;b = 4; 第一步:求整数商c: ①进行求模运算时:c = [a/b] = -7 / 4 = -2(向负无穷方向舍入), ②进行求余运算时:c = [a/b] = -7 / 4 = -1(向0方向舍入); 第二步:计算模和余数的公式相同,但因c的值不同, ①求模时:r = a - cb =-7 - (-2)4 = 1, ②求余时:r = a - cb = -7 - (-1)4 =-3。 例2.计算:7 Mod 4 那么:a = 7;b = 4 第一步:求整数商c: ①进行求模运算c = [a/b] = 7 / 4 = 1 ②进行求余运算c = [a/b] = 7 / 4 = 1 第二步:计算模和余数的公式相同 ①求模时:r = a - cb =7 - (1)4 = 3, ②求余时:r = a - cb = 7 - (1)*4 =3。 归纳:当a和b正负号一致时,求模运算和求余运算所得的c的值一致,因此结果一致。 当正负号不一致时,结果不一样。

另外,%符号在不同的语言中可能含义不同,例如在java,c/c++中表示取余操作,但在python中则表示取模。java中取模就是Math.floorMod()函数. 在golang中,取余操作也是%,但是取模操作需要使用(k%n+n)%n,比如d:=(-10%3+3)%3

四.模运算的实质

先介绍一下“模”的概念:“模”是指一个计量系统的计数范围,如过去计量粮食用的斗、时钟等。实际上计算机也可以看成一个计量机器,因为计算机的字长是定长的,即存储和处理的位数是有限的,因此它也有一个计量范围,即都存在一个“模”。如:时钟的计量范围是0~11,模=12。表示n位的计算机计量范围是,模=.“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法为加法运算.就是取反后加1。

假设当前时针指向8点,而准确时间是6点,调整时间可有以下两种拨法:一种是倒拨2小时,即8-2=6;另一种是顺拨10小时,8+10=12+6=6,即8-2=8+10=8+12-2(mod 12).在12为模的系统里,加10和减2效果是一样的,因此凡是减2运算,都可以用加10来代替。若用一般公式可表示为:a-b=a-b+mod=a+mod-b。对“模”而言,2和10互为补数。实际上,以12为模的系统中,11和1,8和4,9和3,7和5,6和6都有这个特性,共同的特点是两者相加等于模。对于计算机,其概念和方法完全一样。n位计算机,设n=8,所能表示的最大数是11111111,若再加1成100000000(9位),但因只有8位,最高位1自然丢失(相当于丢失一个模)。又回到了 00000000,所以8位二进制系统的模为。在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以了。把补数用到计算机对数的处理上,就是补码 [3].

这里我画了几张图,可以更直观的理解,假设是一个4bit大小的数据结构支持的最大数据的范围: 如果是无符号数,在数轴上表示如下: 无符号数轴 使用圆来表示则如下,这样更符合模的概念,到达最大后会回到原始的位置. 无符号模

有符号数轴 有符号数轴 有符号模 有符号模

从上面就可以发现在计算机中,一个有限长度的数据类型所能表示的所有数据的长度就是它的模,当一个数据不断增大,超过最大值后会回到最小值,比如java中的Integer,占用4byte,也就是32bit,当取最大值Integer.MAX_VALUE(2^31-1)时再加1会变成-2^31 javaInteger溢出后