欢迎光临散文网 会员登陆 & 注册

从汇编代码中探究整数的取余运算

2021-04-28 23:23 作者:刹那-Ksana-  | 我要投稿

前言

之前那一篇文章里面探讨了整数的除法运算,今天我们来再看一下整数的取余运算(modulo);取余运算中,取余的那个数(也就是%后面的那个数)正负号无所谓,所以下文都只谈取余数为正数的情况。

涉及取余数正负号的情况,只出现在当我们两个数的数据类型不一样的时候,比方说unsigned数据取余一个signed的数据,但本文不会谈及这种情况(因为没见过有人这么用)

一、n%1

n%1当然结果是0,就连我们没有优化的-O0都知道:

只不过在这里用的是mov这个指令,而xor会更快一点。

二、n取余2的平方

我们先假设n为一个unsigned类型的数据。

我们知道n%2只会有2种结果,要么是1(n为奇数)要么是0(n为偶数),我们的编译器自然也知道(准确的说是写编译器的人自然也知道),所以会生成以下的汇编代码:

这里是一个非常神奇的and运算(注意,这里的1是十进制下的1),这里的这个and相当于一个mask;我们只需要看最后一个bit,如果最后一位是1(奇数),那么我们就得到1,如果最后一位是0(偶数),那么我们就得到0

我们取余2的p次方时,我们只需要看最后的p个bit,比方说n%8,那么我们就看最后的3个bit,于是就有了:

接下来我们取消我们最初的那个假设,这个时候,n%2会给我们如下的代码:

这个代码突然变得非常复杂,因为我们需要考虑负数的情况。上面这个代码告诉我们,当我们只需要用到非负数取余的时候(比如说loop一个数组),我们最好指明我们的数据类型为unsigned

接下来,我们还是将上面的代码转化为公式:

(n%2BnSHR31)AND1-nSHR31

还是和前文除法运算一样,我们见到了nSHR31这个值,直觉告诉我们这个是运用在负数的情况下的。我们在这里拿n=-1做一个例子,如果我们还是用最上面的那个and运算的话,我们最终得到的结果将会是

-1AND1%3D1

居然变成一个正数了??

但是如果我们用上述的公式的话,我们首先-1+1=0,然后0and1得到0,最后减去1得到-1。这里我们得到了一个正确的值-1。你还可以假设n为其他负数,然后自己算算看,

三、n取余其他整数

n取余其他整数的时候,实际上我们是先做一个除法运算,然后再做一个减法运算

比如说n%3我们会得到如下的代码

前四行我们已经在之前的除法运算一文中说过了——相当于n/3,后三行是

n-3*(n%2F3)

注意,在这里n/3不保留小数点,所以3*(n/3)不等同于n。

所以以上的公式就是我们的取余运算。

后记

写编译器的人果然都是数学大师,能够想得到常人所想不到的。

最后,如果你看上述代码有困难的话,可以参考我做的汇编语言101的视频。

参考工具:Compiler Explorer: https://godbolt.org/

THE END.

从汇编代码中探究整数的取余运算的评论 (共 条)

分享到微博请遵守国家法律