加減乘除簡單嗎?不,一點都不,利用位運算實現加減乘除(代碼中不含+ - * /)

請添加圖片描述

文章目錄

  • 🚀前言
  • 🚀異或運算以及與運算
  • 🚀加法的實現
  • 🚀減法的實現
  • 🚀乘法的實現
  • 🚀除法的實現

🚀前言

這也是阿輝開的新專欄,知識將會很零散不成體系,不過絕對干貨滿滿,今天這一篇利用位運算實現加減乘除費了阿輝九牛二虎之力,干的很自備飲水😆不多bb,進入今天的學習吧!!!


以下int均為有符號int,所求的加減乘除也是int類型的整型數
嚴謹 😏

🚀異或運算以及與運算

在寫加減乘除之前,先給鐵子們介紹一下異或運算以及與運算的其他理解
異或運算:也叫無進位相加
這怎么理解呢?
鐵子們都知道,異或運算,是二進制位相異為1,相同為0

其實異或運算也可以解釋為無進位相加。這是什么意思呢?就是對應的二進制位相加,如果產生進位就將進位舍去。什么是進位呢?對應的二進制位相加為2時就向前進一位,就像我們小時候學的加法一樣。而二進制進位后,本位就剩下一個0。而在異或運算中,進位被舍去,所以當兩個位都為1時,異或的結果為0;當兩個位都為0時,異或的結果也為0;只有一個位為1,另一個位為0時,異或的結果才為1

給鐵子們上圖解釋:
請添加圖片描述
與運算:得到對應二進制位相加的進位信息右移一位后的結果
怎么理解呢?
鐵子們都知道,與運算,是兩二進制位都為1為1,一個為0就為0

實際上,與運算是得到對應二進制位相加的進位信息右移一位后的結果,怎么理解呢?二進制位相加時產生的進位保留,沒有進位舍去直接置0,然后將得到的結果右移一位。

給鐵子們上圖解釋:

請添加圖片描述

🚀加法的實現

有了上述關于異或運算和與運算的新的理解,基于此,我們可以用他們拼湊出加法,怎么拼呢?鐵子們接著看👇

根據前面的講解我們知道,兩個數異或運算得到的是兩個數相加去掉進位信息后的結果,而兩個數與運算得到的是兩個數相加保留進位信息右移一位后的結果,鐵子們,睜大眼騷操作來了😏,異或運算是去掉進位信息的結果,與運算結果再左移一位(進位信息右移了嘛,再左移回去)是保留進位信息的結果,那兩個數異或運算的結果加上兩個數與運算再左移一位的結果不就是兩個數相加的結果嘛

鐵子們一定會說這不還得在加一遍,有毛用,別急還有更騷的😏😏,兩個數的異或運算的結果和與運算左移一位的結果,只要與運算左移一位的結果不為0也就是進位信息不為0,得到的倆個數重復上述操作,直到進位信息為0,異或運算的結果不就是兩數相加的結果嘛。鐵子們,騷不騷?覺得騷的,記得在評論區給阿輝扣個666 😘

肯定有老鐵會說難道進位信息一定會計算到0嗎?好問題哈哈哈哈 😜,一定會計算到0,為什么呢?阿輝用圖說明,鼠標寫的鐵子們湊合看一下🙏
請添加圖片描述

異或運算就是把二進制數相加得到如上述不加上藍色進位信息的結果,然后和進位信息繼續異或,直到不在產生進位信息得到相加的結果

如果還有鐵子不懂,可以私聊阿輝加個微信,阿輝必給你搞明白,上面進位信息為啥一定會算到0是阿輝自己想的,自認還有點騷,哈哈哈 😁!
下面就是代碼實現了,別看講了這一堆,其實代碼很好寫

int Add(int x, int y)//傳入需要需要相加的數
{while (y)//y就是進位信息,為0時跳出循環{int a = x ^ y;//利用臨時變量保證下一步計算進位信息x不變y = (x & y) << 1;//進位信息x = a;//把去掉進位信息的結果賦給a,重復上述操作}return x;
}

代碼就這么短,騷不騷?哈哈哈,這篇博客寫的爽,鐵子們后面的內容更騷,嘿嘿嘿!!!

🚀減法的實現

減法就沒什么技術含量了,因為比如5-4還可以寫成5+(-4),套上前面寫的加法然后,給減數改成相反數即可
相反數怎么整?讓阿輝裝一手,嘿嘿

原反補碼,阿輝就不講了,默認大家都會
按位取反操作符~,一個數取反后,符號位會改變,這么一整,該數的正負相反了,符號位以外的數也取反了,如果再加個1,大家不看符號位這不就是負數原碼得到補碼的過程,喲,這不拿捏了-a = ~a + 1
代碼不就好寫了:

int Sub(int x, int y)//傳入被減數與減數
{return Add(x, ~y + 1);//減數取相反數后與被減數相加
}

🚀乘法的實現

乘法也是稀松平常,沒什么難理解的地方,怎么實現呢?鐵子們別急,阿輝整個例子你們就懂了👊
請添加圖片描述
鐵子們這里阿輝,先給出代碼再解釋:

int Mul(int x, int y)//傳進來兩數
{int ret = 0;//作返回值for (int i = 0; i <= 30; i = Add(i, 1))//符號位不算,固定循環31次{//遍歷y的除符號位的31位,判斷是否為1if (y >> i & 1 == 1)//對應二進制位為1,進入if{//因為二進制位只有0和1,0乘任何數還是0,所以不用管//當為1時,1乘任何數還是它本身,所以加上xret = Add(ret, x);}//遍歷一次x左移一位,因為再循環,y向后一位找1//這個1代表2的i次方,相當于把這個2的i次方乘在了x上//左移一位相當于乘2,右移一位相當于除2x <<= 1;}return ret;
}

鐵子們注意,上述實現的乘法可以計算負數,為什么呢?這與原反補碼有關,阿輝水平有限解釋不出來,鐵子們感興趣可以研究一下,找到記得和阿輝分享一波,嘿嘿
鐵子們,沒懂的話可以自己想想試試,如果實在不懂可以私信我好吧,下面的除法才是重頭戲特別麻煩 💀

🚀除法的實現

除法比較麻煩,這里阿輝實現的除法是整數除法,上圖給大家引導:
請添加圖片描述
關于將除法抽象成代碼,這是一個相當復雜的過程。首先,讓我們回顧一下如何進行除法運算。在我們最常見的十進制系統中,以被除數728÷8為例,我們是如何得出十位上的9的呢?除法運算實際上是在找到一個最大的數,使得將除數8乘以這個數接近被除數728,然后再用728減去這個數720(8×90),然后繼續這個過程,要么除盡要么除不盡。不過在整數除法中,如果除不盡就舍去余數。實際上,二進制數的除法運算也是類似的過程。與十進制不同的是,二進制位只有1,因此只需要將除數101后面加上0來逼近被除數101001011(在二進制中,將除數101左移一位就相當于除數后面加上一個0),然后用101001011減去101×1000000(101左移6位),然后重復這個操作直到除盡

但是我們在寫代碼時不能通過除數左移去逼近,因為這樣會有風險
請添加圖片描述
紅色方塊的位置代表符號位。我們給除數b左移,左移一位比a小,繼續左移直到比a大才知道上一次左移逼近被除數a。但是對于我們給的這個例子b怎么左移也不可能比a大,因為當b右移11位時,符號位變成1了,b直接變成負數了。
這里我們通過被除數右移代替除數左移來逼近除數達到一樣的效果,因為被除數右移多少位逼近除數也就是除數左移多少位逼近被除數,這樣就不會有改變符號位的風險
請添加圖片描述
我們讓除數a從右移14位開始,然后右移位數依次遞減,當a右移10位時第一次大于b也就是b左移10位逼近a,然后a減去b左移10位后的數得到的結果重復上述操作第一次找到的就是最高位的1就像我們算十進制除法一樣,我們首先找到千位隨后就是百位、十位只不過二進制只有0和1很多位都是0

鐵子們,下面我會根據代碼講,盡我所能講明白好吧!
首先,我們實現的除法并不能處理負數,要先把負數處理成正數
這里我們實現一個函數oppo()求相反數

int oppo(int x)//相反數
{return Add(~x, 1);//上面減法的時候解釋過了
}

下面是關于除法的具體實現(不包括系統最小值)

int dived(int x, int y)//不含系統最小數的除法
{//負數改正數,正數則不變int a = x < 0 ? oppo(x) : x;//小于0就取相反數int b = y < 0 ? oppo(y) : y;int ret = 0;//作返回值//除了符號位,遍歷31次for (int i = 30; i >= 0; i = Sub(i, 1)){//i從30開始依次遞減//當a第一次右移i位大于b時,說明最高位的數字找到了//進入if把值加到返回值ret上if (a >> i >= b){ret |= 1 << i;//ret初始值時0,把1或上去a = Sub(a, Mul(b,ret));//同時更新a的值,a=a-b×ret}}//只有x和y不同時為負數或正數時要取相反數//x,y同時大于或小于0,相除就是正數嘛//異或值相等為0嘛就是假//這個異或騷不騷,代替了下面這么挫的代碼//if((x > 0 && y < 0) || (x < 0 && y > 0))if (x > 0 ^ y > 0)return oppo(ret);//取相反數return ret;
}

包含系統最小值,int類型的取值為-2^30^ ~ 0 ~ 2^30^-1,因為0是包含在正數里的,所以系統最小值的絕對值大于系統最大值,所以我們面臨一個問題,系統最小值沒有它的相反數,所以求系統最小值我們要單獨拎出來求
五種情況(x是被除數,y是除數):

  • x,y都是系統最小,直接返回1
  • y是系統最小,x不是,直接返回0(整數除法)
  • x是系統最小,y是-1,那結果就是系統最小的絕對值,值域沒這個數,直接返回-1
  • x,y都不是系統最小,咱們上面實現的那個代碼就是
  • x是系統最小,y不是,這個情況就是我們下面要解決的

x是系統最小,y不是,這里我們無法給它取相反數,我們給x拆成兩部分,a=(x+1)得到的就不是系統最小了,用b = (a÷y),可以用我們上面的dived()這個函數來求,然后在用c = x - (b × y),接著a = c ÷ y ,最后a+b就是x÷y的結果
請添加圖片描述
就是上面這張圖這個原理,沒啥難的,不一定要加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);}elsereturn dived(x,y);//不含系統最小值的除法
}

coding有三點要注意:

  • 負數要先轉化成正數
  • 被除數左移會有風險
  • 系統最小無法取相反數

加減乘除的完整代碼,他們并不是孤立的,互相調用

#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,ret));}}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);}elsereturn dived(x,y);
}

好的,到這里阿輝就講完了,說實話不容易,著力于構思怎么把鐵子們講懂,應該沒有比我更細節的了,寫完這篇滿滿成就感,鐵子們覺得講得不錯的話給阿輝評論區扣個666,哈哈哈!!!!
請添加圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/215943.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/215943.shtml
英文地址,請注明出處:http://en.pswp.cn/news/215943.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

華為鴻蒙HarmonyOS應用開發者高級認證試題及答案

判斷 1只要使用端云一體化的云端資源就需要支付費用&#xff08;錯&#xff09; 2所有使用Component修飾的自定義組件都支持onPageShow&#xff0c;onBackPress和onPageHide生命周期函數。&#xff08;錯&#xff09; 3 HarmonyOS應用可以兼容OpenHarmony生態&#xff08;對…

多維時序 | MATLAB實現SAO-CNN-BiGRU-Multihead-Attention多頭注意力機制多變量時間序列預測

多維時序 | MATLAB實現SAO-CNN-BiGRU-Multihead-Attention多頭注意力機制多變量時間序列預測 目錄 多維時序 | MATLAB實現SAO-CNN-BiGRU-Multihead-Attention多頭注意力機制多變量時間序列預測預測效果基本介紹模型描述程序設計參考資料 預測效果 基本介紹 MATLAB實現SAO-CNN-B…

CommonJs模塊化實現原理ES Module模塊化原理

CommonJs模塊化實現原理 首先看一個案例 初始化項目 npm init npm i webpack -D目錄結構如下&#xff1a; webpack.config.js const path require("path"); module.exports {mode: "development",entry: "./src/index.js",output: {path: p…

硬件開發筆記(十六):RK3568底板電路mipi攝像頭接口原理圖分析、mipi攝像頭詳解

若該文為原創文章&#xff0c;轉載請注明原文出處 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/134922307 紅胖子網絡科技博文大全&#xff1a;開發技術集合&#xff08;包含Qt實用技術、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機、軟硬…

Redis緩存主要異常及解決方案

1 導讀 Redis 是當前最流行的 NoSQL數據庫。Redis主要用來做緩存使用,在提高數據查詢效率、保護數據庫等方面起到了關鍵性的作用,很大程度上提高系統的性能。當然在使用過程中,也會出現一些異常情景,導致Redis失去緩存作用。 2 異常類型 異常主要有 緩存雪崩 緩存穿透 緩…

【sqli靶場】第二關和第三關通關思路

目錄 前言 一、sqli靶場第二關 1.1 判斷注入類型 1.2 判斷數據表中的列數 1.3 使用union聯合查詢 1.4 使用group_concat()函數 1.5 爆出users表中的列名 1.6 爆出users表中的數據 二、sqli靶場第三關 2.1 判斷注入類型 2.2 觀察報錯 2.3 判斷數據表中的列數 2.4 使用union聯合…

Emutouch學習筆記

1 項目依賴 DeviceFarmer/minitouch 1.1 確認submodule引用的 commit ID git submodule status1.2 更新子模塊到最新版本 git submodule init && git submodule update --remote

Android:監聽開機廣播自己喚醒

要通過代碼獲取安卓系統的開機廣播消息&#xff0c;并在收到消息后拉起當前apk&#xff0c;您可以使用以下步驟&#xff1a; 創建一個廣播接收器&#xff08;Broadcast Receiver&#xff09;來接收開機廣播消息。在接收到開機廣播消息時&#xff0c;您可以在接收器中編寫代碼來…

什么是 web 組態?web 組態與傳統組態的區別是什么?

組態軟件是一種用于控制和監控各種設備的軟件&#xff0c;也是指在自動控制系統監控層一級的軟件平臺和開發環境。這類軟件實際上也是一種通過靈活的組態方式&#xff0c;為用戶提供快速構建工業自動控制系統監控功能的、通用層次的軟件工具。通常用于工業控制&#xff0c;自動…

Spring Boot整合 Spring Security

Spring Boot整合 1、RBAC 權限模型 RBAC模型&#xff08;Role-Based Access Control&#xff1a;基于角色的訪問控制&#xff09; 在RBAC模型里面&#xff0c;有3個基礎組成部分&#xff0c;分別是&#xff1a;用戶、角色和權限&#xff0c;它們之間的關系如下圖所示 SELECT…

02.類模板

2、類模板 2.1 類模板語法 建立一個通用類&#xff0c;類中的成員、數據類型可以不具體制定&#xff0c;用一個虛擬的類型來代表。 template<typename T> // 類template&#xff1a;聲明創建模板typename&#xff1a;表名其后面的符號是一種數據類型&#xff0c;可以用 …

【算法】算法題-20231211

這里寫目錄標題 一、387. 字符串中的第一個唯一字符二、1189. “氣球” 的最大數量三、1221. 分割平衡字符串 一、387. 字符串中的第一個唯一字符 簡單 給定一個字符串 s &#xff0c;找到 它的第一個不重復的字符&#xff0c;并返回它的索引 。如果不存在&#xff0c;則返回…

算法通關村第十五關 | 青銅 | 用4KB內存尋找重復元素

處理海量數據的思路 1.使用位存儲&#xff1a;占用的空間是存整數的 1/8 。 2.分塊&#xff1a;也叫外部排序&#xff0c;將大文件劃分為若干小塊&#xff0c;先處理小塊再逐步得到想要的結果&#xff0c;需要至少遍歷兩次全部序列&#xff0c;是用時間換空間的方法。 3.堆&…

Mockjs 增、刪、改、查(分頁、多條件查詢)

查&#xff08;分頁、多條件查詢&#xff09;&#xff1a; 關鍵代碼&#xff1a; Mock.mock(/vue-table-list/tableLinkage/list, post, (option) > {// console.log("&#x1f680; ~ file: tableLinkage.js:66 ~ Mock.mock ~ option:", option)const params J…

MFC畫折線圖,基于x64系統

由于項目的需要&#xff0c;需要畫一個折線圖。 傳統的Teechart、MSChart、HighSpeedChart一般是只能配置在x86系統下&#xff0c;等到使用x64系統下運行就是會報出不知名的錯誤&#xff0c;這個地方讓人很苦惱。 我在進行配置的過程之中&#xff0c;使用Teechart將x86配置好…

基于Java SSM框架實現班級同學錄、聚會報名網站系統項目【項目源碼+論文說明】

基于java的SSM框架實現班級同學錄聚會報名網站系統演示 摘要 21世紀的今天&#xff0c;隨著社會的不斷發展與進步&#xff0c;人們對于信息科學化的認識&#xff0c;已由低層次向高層次發展&#xff0c;由原來的感性認識向理性認識提高&#xff0c;管理工作的重要性已逐漸被人…

程序員考公筆記之邏輯判斷(圖形推理)

文章目錄 寫在前面1、邏輯判斷1.1、圖形推理1.1.1、位置類1.1.2、樣式類1.1.3、數量類1.1.4、屬性類1.1.5、六面體 寫在前面 1、邏輯判斷 1.1、圖形推理 觀察&#xff1a;先宏觀&#xff0c;再微觀 圖形推理的命題形式&#xff1a; 一組式 觀察路徑&#xff1a;順序看(考最…

解決方案- 材料吸波、屏蔽性能測試系統 (10MHz~500GHz)

材料吸波、屏蔽性能測試系統 &#xff08;10MHz~500GHz&#xff09; 材料電磁參數綜合測試解決方案 材料吸波、屏蔽性能測試系統測試頻率范圍可達10MHz&#xff5e;500GHz&#xff0c;可實現材料反射率、屏蔽性能特性參數測試。系統由矢量網絡分析儀、測試夾具、系統軟件等組…

申論筆記(思路技巧)

文章目錄&#xff1a; 一&#xff1a;福利 二&#xff1a;常見題型 1.歸納概括題 2.提出對策/措施/建議題 2.1 找到對策的來源 2.2 提煉對策 2.3 明確是否需要先概括問題 2.4 對策表述三部曲 3.綜合分析題 3.1 綜合分析最大的難點 3.2 分析問題的技巧 4.應用文/公文…

力扣每日一題day34[110. 平衡二叉樹]

給定一個二叉樹&#xff0c;判斷它是否是高度平衡的二叉樹。 本題中&#xff0c;一棵高度平衡二叉樹定義為&#xff1a; 一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過 1 。 示例 1&#xff1a; 輸入&#xff1a;root [3,9,20,null,null,15,7] 輸出&#xff1a;t…