??前言
這也是阿輝開(kāi)的新專欄,知識(shí)將會(huì)很零散不成體系,不過(guò)絕對(duì)干貨滿滿,今天這一篇利用位運(yùn)算實(shí)現(xiàn)加減乘除費(fèi)了阿輝九牛二虎之力,干的很自備飲水??不多bb,進(jìn)入今天的學(xué)習(xí)吧?。?!
以下int均為有符號(hào)int,所求的加減乘除也是int類型的整型數(shù)
嚴(yán)謹(jǐn) ??
??異或運(yùn)算以及與運(yùn)算
在寫加減乘除之前,先給鐵子們介紹一下異或運(yùn)算以及與運(yùn)算的其他理解
異或運(yùn)算:也叫無(wú)進(jìn)位相加
這怎么理解呢?
鐵子們都知道,異或運(yùn)算,是二進(jìn)制位相異為1,相同為0
其實(shí)異或運(yùn)算也可以解釋為無(wú)進(jìn)位相加。這是什么意思呢?就是對(duì)應(yīng)的二進(jìn)制位相加,如果產(chǎn)生進(jìn)位就將進(jìn)位舍去。什么是進(jìn)位呢?對(duì)應(yīng)的二進(jìn)制位相加為2時(shí)就向前進(jìn)一位,就像我們小時(shí)候?qū)W的加法一樣。而二進(jìn)制進(jìn)位后,本位就剩下一個(gè)0。而在異或運(yùn)算中,進(jìn)位被舍去,所以當(dāng)兩個(gè)位都為1時(shí),異或的結(jié)果為0;當(dāng)兩個(gè)位都為0時(shí),異或的結(jié)果也為0;只有一個(gè)位為1,另一個(gè)位為0時(shí),異或的結(jié)果才為1
給鐵子們上圖解釋:
與運(yùn)算:得到對(duì)應(yīng)二進(jìn)制位相加的進(jìn)位信息右移一位后的結(jié)果
怎么理解呢?
鐵子們都知道,與運(yùn)算,是兩二進(jìn)制位都為1為1,一個(gè)為0就為0
實(shí)際上,與運(yùn)算是得到對(duì)應(yīng)二進(jìn)制位相加的進(jìn)位信息右移一位后的結(jié)果,怎么理解呢?二進(jìn)制位相加時(shí)產(chǎn)生的進(jìn)位保留,沒(méi)有進(jìn)位舍去直接置0,然后將得到的結(jié)果右移一位。
給鐵子們上圖解釋:
??加法的實(shí)現(xiàn)
有了上述關(guān)于異或運(yùn)算和與運(yùn)算的新的理解,基于此,我們可以用他們拼湊出加法,怎么拼呢?鐵子們接著看??
根據(jù)前面的講解我們知道,兩個(gè)數(shù)異或運(yùn)算得到的是兩個(gè)數(shù)相加去掉進(jìn)位信息后的結(jié)果,而兩個(gè)數(shù)與運(yùn)算得到的是兩個(gè)數(shù)相加保留進(jìn)位信息右移一位后的結(jié)果,鐵子們,睜大眼騷操作來(lái)了??,異或運(yùn)算是去掉進(jìn)位信息的結(jié)果,與運(yùn)算結(jié)果再左移一位(進(jìn)位信息右移了嘛,再左移回去)是保留進(jìn)位信息的結(jié)果,那兩個(gè)數(shù)異或運(yùn)算的結(jié)果加上兩個(gè)數(shù)與運(yùn)算再左移一位的結(jié)果不就是兩個(gè)數(shù)相加的結(jié)果嘛;
鐵子們一定會(huì)說(shuō)這不還得在加一遍,有毛用,別急還有更騷的????,兩個(gè)數(shù)的異或運(yùn)算的結(jié)果和與運(yùn)算左移一位的結(jié)果,只要與運(yùn)算左移一位的結(jié)果不為0也就是進(jìn)位信息不為0,得到的倆個(gè)數(shù)重復(fù)上述操作,直到進(jìn)位信息為0,異或運(yùn)算的結(jié)果不就是兩數(shù)相加的結(jié)果嘛。鐵子們,騷不騷?覺(jué)得騷的,記得在評(píng)論區(qū)給阿輝扣個(gè)666 ??
肯定有老鐵會(huì)說(shuō)難道進(jìn)位信息一定會(huì)計(jì)算到0嗎?好問(wèn)題哈哈哈哈 ??,一定會(huì)計(jì)算到0,為什么呢?阿輝用圖說(shuō)明,鼠標(biāo)寫的鐵子們湊合看一下??
異或運(yùn)算就是把二進(jìn)制數(shù)相加得到如上述不加上藍(lán)色進(jìn)位信息的結(jié)果,然后和進(jìn)位信息繼續(xù)異或,直到不在產(chǎn)生進(jìn)位信息得到相加的結(jié)果
如果還有鐵子不懂,可以私聊阿輝加個(gè)微信,阿輝必給你搞明白,上面進(jìn)位信息為啥一定會(huì)算到0是阿輝自己想的,自認(rèn)還有點(diǎn)騷,哈哈哈 ??!
下面就是代碼實(shí)現(xiàn)了,別看講了這一堆,其實(shí)代碼很好寫
int Add(int x, int y)//傳入需要需要相加的數(shù)
{
while (y)//y就是進(jìn)位信息,為0時(shí)跳出循環(huán)
{
int a = x ^ y;//利用臨時(shí)變量保證下一步計(jì)算進(jìn)位信息x不變
y = (x & y) << 1;//進(jìn)位信息
x = a;//把去掉進(jìn)位信息的結(jié)果賦給a,重復(fù)上述操作
}
return x;
}
代碼就這么短,騷不騷?哈哈哈,這篇博客寫的爽,鐵子們后面的內(nèi)容更騷,嘿嘿嘿!?。?/p>
??減法的實(shí)現(xiàn)
減法就沒(méi)什么技術(shù)含量了,因?yàn)楸热?kbd>5-4還可以寫成5+(-4),套上前面寫的加法然后,給減數(shù)改成相反數(shù)即可
相反數(shù)怎么整?讓阿輝裝一手,嘿嘿
原反補(bǔ)碼,阿輝就不講了,默認(rèn)大家都會(huì)
按位取反操作符~
,一個(gè)數(shù)取反后,符號(hào)位會(huì)改變,這么一整,該數(shù)的正負(fù)相反了,符號(hào)位以外的數(shù)也取反了,如果再加個(gè)1,大家不看符號(hào)位這不就是負(fù)數(shù)原碼得到補(bǔ)碼的過(guò)程,喲,這不拿捏了-a = ~a + 1
代碼不就好寫了:
int Sub(int x, int y)//傳入被減數(shù)與減數(shù)
{
return Add(x, ~y + 1);//減數(shù)取相反數(shù)后與被減數(shù)相加
}
??乘法的實(shí)現(xiàn)
乘法也是稀松平常,沒(méi)什么難理解的地方,怎么實(shí)現(xiàn)呢?鐵子們別急,阿輝整個(gè)例子你們就懂了??
鐵子們這里阿輝,先給出代碼再解釋:
int Mul(int x, int y)//傳進(jìn)來(lái)兩數(shù)
{
int ret = 0;//作返回值
for (int i = 0; i <= 30; i = Add(i, 1))//符號(hào)位不算,固定循環(huán)31次
{
//遍歷y的除符號(hào)位的31位,判斷是否為1
if (y >> i & 1 == 1)//對(duì)應(yīng)二進(jìn)制位為1,進(jìn)入if
{
//因?yàn)槎M(jìn)制位只有0和1,0乘任何數(shù)還是0,所以不用管
//當(dāng)為1時(shí),1乘任何數(shù)還是它本身,所以加上x
ret = Add(ret, x);
}
//遍歷一次x左移一位,因?yàn)樵傺h(huán),y向后一位找1
//這個(gè)1代表2的i次方,相當(dāng)于把這個(gè)2的i次方乘在了x上
//左移一位相當(dāng)于乘2,右移一位相當(dāng)于除2
x <<= 1;
}
return ret;
}
鐵子們注意,上述實(shí)現(xiàn)的乘法可以計(jì)算負(fù)數(shù),為什么呢?這與原反補(bǔ)碼有關(guān),阿輝水平有限解釋不出來(lái),鐵子們感興趣可以研究一下,找到記得和阿輝分享一波,嘿嘿
鐵子們,沒(méi)懂的話可以自己想想試試,如果實(shí)在不懂可以私信我好吧,下面的除法才是重頭戲特別麻煩 ??
??除法的實(shí)現(xiàn)
除法比較麻煩,這里阿輝實(shí)現(xiàn)的除法是整數(shù)除法,上圖給大家引導(dǎo):
關(guān)于將除法抽象成代碼,這是一個(gè)相當(dāng)復(fù)雜的過(guò)程。首先,讓我們回顧一下如何進(jìn)行除法運(yùn)算。在我們最常見(jiàn)的十進(jìn)制系統(tǒng)中,以被除數(shù)728
÷8
為例,我們是如何得出十位上的9
的呢?除法運(yùn)算實(shí)際上是在找到一個(gè)最大的數(shù),使得將除數(shù)8
乘以這個(gè)數(shù)接近被除數(shù)728
,然后再用728
減去這個(gè)數(shù)720
(8×90),然后繼續(xù)這個(gè)過(guò)程,要么除盡要么除不盡。不過(guò)在整數(shù)除法中,如果除不盡就舍去余數(shù)。實(shí)際上,二進(jìn)制數(shù)的除法運(yùn)算也是類似的過(guò)程。與十進(jìn)制不同的是,二進(jìn)制位只有1,因此只需要將除數(shù)101
后面加上0
來(lái)逼近被除數(shù)101001011
(在二進(jìn)制中,將除數(shù)101
左移一位就相當(dāng)于除數(shù)后面加上一個(gè)0
),然后用101001011
減去101×1000000
(101左移6位),然后重復(fù)這個(gè)操作直到除盡
但是我們?cè)趯懘a時(shí)不能通過(guò)除數(shù)左移去逼近,因?yàn)檫@樣會(huì)有風(fēng)險(xiǎn)
紅色方塊的位置代表符號(hào)位。我們給除數(shù)b左移,左移一位比a小,繼續(xù)左移直到比a大才知道上一次左移逼近被除數(shù)a。但是對(duì)于我們給的這個(gè)例子b怎么左移也不可能比a大,因?yàn)楫?dāng)b右移11位時(shí),符號(hào)位變成1
了,b直接變成負(fù)數(shù)了。
這里我們通過(guò)被除數(shù)右移代替除數(shù)左移來(lái)逼近除數(shù)達(dá)到一樣的效果,因?yàn)楸怀龜?shù)右移多少位逼近除數(shù)也就是除數(shù)左移多少位逼近被除數(shù),這樣就不會(huì)有改變符號(hào)位的風(fēng)險(xiǎn)
我們讓除數(shù)a從右移14位
開(kāi)始,然后右移位數(shù)依次遞減,當(dāng)a右移10位時(shí)第一次大于b
也就是b左移10位逼近a
,然后a減去b左移10位后的數(shù)得到的結(jié)果重復(fù)上述操作
第一次找到的就是最高位的1
就像我們算十進(jìn)制除法一樣,我們首先找到千位
隨后就是百位、十位
只不過(guò)二進(jìn)制只有0和1
很多位都是0
鐵子們,下面我會(huì)根據(jù)代碼講,盡我所能講明白好吧!
首先,我們實(shí)現(xiàn)的除法并不能處理負(fù)數(shù),要先把負(fù)數(shù)處理成正數(shù)
這里我們實(shí)現(xiàn)一個(gè)函數(shù)oppo()
求相反數(shù)
int oppo(int x)//相反數(shù)
{
return Add(~x, 1);//上面減法的時(shí)候解釋過(guò)了
}
下面是關(guān)于除法的具體實(shí)現(xiàn)(不包括系統(tǒng)最小值)
int dived(int x, int y)//不含系統(tǒng)最小數(shù)的除法
{
//負(fù)數(shù)改正數(shù),正數(shù)則不變
int a = x < 0 ? oppo(x) : x;//小于0就取相反數(shù)
int b = y < 0 ? oppo(y) : y;
int ret = 0;//作返回值
//除了符號(hào)位,遍歷31次
for (int i = 30; i >= 0; i = Sub(i, 1))
{
//i從30開(kāi)始依次遞減
//當(dāng)a第一次右移i位大于b時(shí),說(shuō)明最高位的數(shù)字找到了
//進(jìn)入if把值加到返回值ret上
if (a >> i >= b)
{
ret |= 1 << i;//ret初始值時(shí)0,把1或上去
a = Sub(a, Mul(b,ret));//同時(shí)更新a的值,a=a-b×ret
}
}
//只有x和y不同時(shí)為負(fù)數(shù)或正數(shù)時(shí)要取相反數(shù)
//x,y同時(shí)大于或小于0,相除就是正數(shù)嘛
//異或值相等為0嘛就是假
//這個(gè)異或騷不騷,代替了下面這么挫的代碼
//if((x > 0 && y < 0) || (x < 0 && y > 0))
if (x > 0 ^ y > 0)
return oppo(ret);//取相反數(shù)
return ret;
}
包含系統(tǒng)最小值,int
類型的取值為-2^30^ ~ 0 ~ 2^30^-1
,因?yàn)?是包含在正數(shù)里的,所以系統(tǒng)最小值的絕對(duì)值大于系統(tǒng)最大值,所以我們面臨一個(gè)問(wèn)題,系統(tǒng)最小值沒(méi)有它的相反數(shù),所以求系統(tǒng)最小值我們要單獨(dú)拎出來(lái)求
五種情況(x是被除數(shù),y是除數(shù)):
- x,y都是系統(tǒng)最小,直接返回
1
- y是系統(tǒng)最小,x不是,直接返回
0
(整數(shù)除法) - x是系統(tǒng)最小,y是-1,那結(jié)果就是系統(tǒng)最小的絕對(duì)值,值域沒(méi)這個(gè)數(shù),直接返回-1
- x,y都不是系統(tǒng)最小,咱們上面實(shí)現(xiàn)的那個(gè)代碼就是
- x是系統(tǒng)最小,y不是,這個(gè)情況就是我們下面要解決的
x是系統(tǒng)最小,y不是,這里我們無(wú)法給它取相反數(shù),我們給x拆成兩部分,
a=(x+1)
得到的就不是系統(tǒng)最小了,用b = (a÷y),可以用我們上面的dived()
這個(gè)函數(shù)來(lái)求,然后在用c = x - (b × y)
,接著a = c ÷ y
,最后a+b
就是x÷y
的結(jié)果
就是上面這張圖這個(gè)原理,沒(méi)啥難的,不一定要加1,2、3、4……都可以
INT_MIN被宏定義成int類型的最小值,使用需要引<limits.h>
頭文件
int Div(int x, int y)
{
if (x == INT_MIN && y == INT_MIN)
return 1;
else if (x == INT_MIN && y == -1)
return -1;
else if (y == INT_MIN)
return 0;
//這就是上面解釋的代碼
else if (x == INT_MIN)
{
int a = Add(x, 1);
int b = dived(a, y);
a = dived(Sub(x, Mul(b, y)), y);
return Add(a, b);
}
else
return dived(x,y);//不含系統(tǒng)最小值的除法
}
coding有三點(diǎn)要注意:
- 負(fù)數(shù)要先轉(zhuǎn)化成正數(shù)
- 被除數(shù)左移會(huì)有風(fēng)險(xiǎn)
- 系統(tǒng)最小無(wú)法取相反數(shù)
加減乘除的完整代碼,他們并不是孤立的,互相調(diào)用文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-775296.html
#include<stdio.h>
#include<limits.h>
int Add(int x, int y)
{
while (y)
{
int a = x ^ y;
y = (x & y) << 1;
x = a;
}
return x;
}
int Sub(int x, int y)
{
return Add(x, Add(~y, 1));
}
int Mul(int x, int y)
{
int ret = 0;
for (int i = 0; i <= 30; i = Add(i, 1))
{
if (y >> i & 1 == 1)
{
ret = Add(ret, x);
}
x <<= 1;
}
return ret;
}
int oppo(int x)
{
return Add(~x, 1);
}
int dived(int x, int y)
{
int a = x < 0 ? oppo(x) : x;
int b = y < 0 ? oppo(y) : y;
int ret = 0;
for (int i = 30; i >= 0; i = Sub(i, 1))
{
if (a >> i >= b)
{
ret |= 1 << i;
a = Sub(a, Mul(b,1 << i));
}
}
if (x > 0 ^ y > 0)
return oppo(ret);
return ret;
}
int Div(int x, int y)
{
if (x == INT_MIN && y == INT_MIN)
return 1;
else if (x == INT_MIN && y == -1)
return -1;
else if (y == INT_MIN)
return 0;
else if (x == INT_MIN)
{
int a = Add(x, 1);
int b = dived(a, y);
a = dived(Sub(x, Mul(b, y)), y);
if (x > 0 ^ y > 0)
return oppo(Add(a, b));
return Add(a, b);
}
else
return dived(x,y);
}
好的,到這里阿輝就講完了,說(shuō)實(shí)話不容易,著力于構(gòu)思怎么把鐵子們講懂,應(yīng)該沒(méi)有比我更細(xì)節(jié)的了,寫完這篇滿滿成就感,鐵子們覺(jué)得講得不錯(cuò)的話給阿輝評(píng)論區(qū)扣個(gè)666,哈哈哈?。。。?br>文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-775296.html
到了這里,關(guān)于加減乘除簡(jiǎn)單嗎?不,一點(diǎn)都不,利用位運(yùn)算實(shí)現(xiàn)加減乘除(代碼中不含+ - * /)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!