位运算算法篇:位运算实现加减乘除
位运算算法篇:位运算实现加减乘除
那么我们想必对加减乘除这些数学计算并不陌生,但是对于我们的计算机来说,由于机器只能识别二进制的语言,那么我们底层的数据都是以二进制的形式存在,那么我们CPU的计算器的加减乘除运算肯定都是通过位运算的方式来实现的,那么本篇文章就是用位运算来实现我们的加减乘除运算,那么废话不多说,我们进入正文
1.加法运算
那么在我们的现实生活中我们熟悉我们十进制的加法运算,运算规则就是对应的十进制位进行相加,然后逢十进一,进位到下一位上去,而由于我们计算机的各种数据都是以二进制序列的形式存在,所以我们的加法运算实际上就是要实现二进制的加法运算。
那么要实现二进制的加法运算的话,我们首先得知道二进制加法运算的规则,那么二进制加法运算的规则就是对应二进制位相加,逢二进一,那么这里我们只能通过我们的位运算来实现,那么我们的位运算无非就是与,或,非以及异或运算等等,那么我们怎么通过这些位运算来实现我们加法运算的一个逻辑呢。
其中异或运算我专门花了一片文章来讲解异或运算的原理以及应用,那么我们知道异或运算的本质就是无进位相加,那么对于实现我们二进制的加法运算,那么我们可以采取这样的一个思路,那么我们知道我们两个数进行异或运算就相当于两个数进行一个无进位相加的加法运算,那么此时我们这里进行完一次异或运算,我们相当于得到了每个对应二进制位相加的结果,但是唯独每一个二进制位没有加上进位信息,那么也就意味着只要我们将我们异或运算的结果得到的二进制序列的每一位再对应加上进位信息,那么就是我们的正确结果。
那么这里的进位信息,我们知道只有两个二进制位都为1那么会产生进位,所以这里我们假设我们a和b两个数做加法运算,那么我们要得到a+b的进位信息的话,我们可以a & b,那么与运算的规则是对应二进制位都为1结果才能为1,只要有一个二进制位为0结果就为0,那么我们a&b的结果会得到一个二进制序列,那么这个二进制序列的每一位的0和1就表示该二进制位相加如果有进位,那么该二进制位就是1,反之为0,。
那么我们实现加法运算的最后关键一步就是刚才异或运算无进位相加的结果再异或上与运算的进位信息即可,但是与运算得到的二进制序列中的0和1表示是该二进制位是否有进位,但是实际要相加的话,因为进位是要进到下一位,所以我们就得将与运算的二进制序列给左移一位,然后加上异或运算的序列。
那么可能有的读者就有疑问,那么我们加完万一还会有进位怎么办,比如:假设是4个二进制位的两个数相加
1011+0001,这里我们最低位会产生一个进位到第二位,那么第二位加上我们的进位又会产生一个进位,而我们刚才的思路的话,我们将1011与0001先异或运算得到1010,然后将1011与0001在与运算得到0001,然后左移一位得到0010,那么我们将刚才得到的异或运算的结果1010在与我们与运算右移的结果再进行异或:1010 ^ 0010 = 1000,但是我们知道1011+0001的答案实际上是1100.
所以此时将我们的刚才计算的结果1000与之前的与运算右移的结果0010重新作为新的两个加数,异或完后得再一次与运算一下,确定运算的结果是否为0,如果不为0,说明还有进位,那么就得在加上之前的进位信息,所以重复之前的步骤,先异或,再与运算然后左移,再异或:
1000 ^ 0010 =1010
(1000 & 0010)<<1=0000
1010 ^ 0000 =1010
那么我们此时将我们的结果1010和0000重新作为加数,但是我们如果得到进位信息为0,也就是没有进位,那么我们计算就结束,那么这里就是我们代码设计循环的一个终止条件。
代码实现:
#include<iostream>
using namespace std;
int add(int a,int b)
{while(b!=0){a=a^b;b=(a&b)<<1;}return a;
}
int main()
{int a=10,b=30;cout<<a<<"+"<<b<<"="<<endl;int res=add(a,b);cout<<res<<endl;
}
2.减法运算
那么我们在位运算的第一篇就讲到过,由于负数的补码专门进行了处理,所以一个数减去一个数,可以看成一个数加上该数的负数,那么我们减法运算就可以应用我们加法运算的逻辑,这里注意一个正数得到负数我们就取反加一即可:
#include<iostream>
using namespace std;
int add(int a,int b)
{while(b!=0){int carry=(a&b)<<1;a=a^b;b=carry;}return a;
}
int main()
{int a=10,b=30;cout<<a<<"-"<<b<<"="<<endl;int res=add(a,(~b)+1);cout<<res<<endl;
}
3.乘法运算
那么我们对于十进制的乘法运算我们十分熟悉,那么可能对于二进制的乘法运算的话我们就显得稍微有点陌生,那么我们二进制的乘法运算和我们十进制的乘法运算的运算规则是一样的,也就是每一个二进制位来与另一个数相乘。
以四位二进制数为例
1001
0110
————
0
1001
1001
0
————
011 0
那么我们则如何实现我们乘法运算的这套逻辑呢,那么我们知道我们乘法运算的话,那么如果该二进制位是1,那么则是另一个乘数本身,反之如果是0,那么则是0,所以我们要现将这个乘数的每一个二进制位来判断,所以得将其与1进行与运算判断该位置是0还是1,然后将下一个要判断的二进制位移到最低位,然后我们要将另一个乘数给左移同样的步数,因为左移之后右边是用0来补,所以我们左移完的结果在与之前的数相加即可
代码实现:
#include<iostream>
using namespace std;
int mulpitly(int a,int b)
{int i=0;int res=0;while(a!=0){int ant=a&1;if(ant!=0){res+=(b<<i);}i++;a=a>>1;}return res;
}
int main()
{int a=10,b=30;cout<<a<<"*"<<b<<"="<<endl;int res=mulpitly(a,b);cout<<res<<endl;
}
4.除法运算
那么我们除法运算是我们加减乘除这4个运算中最难实现的一个运算,其中难于实现的原因就是很多人它不知道我们二进制的除法的规则是什么,那么你要问我是具体怎么实现我们的除法运算,那么我的思路不是从我们除法本身的规则除法,那么我们在实现除法运算之前,我们前面已经有了我们的加法以及减法和乘法运算,那么我们谁说一定就要按照二进制本身除法的运算规则去实现它,我们可以转换为我们的乘法以及加法来实现我们的除法。
那么可能读者看到这还是内心里还是有点疑惑,那么我接着将一步阐述我实现除法的一个原理:
那么我们假设我们有两个不同的数比如a和b,那么我们a与b做除法运算,那么这里我们假设a是被除数,b是除数,并且a不是整除b的,我们除法运算不就是要得到a/b的尚以及余数吗?那么我们知道a/b会得到一个商和余数,那么意味着我们a除以b我们可以写成:a=b*商+余数
那么现在我们首先求这个商的值,那么我们知道我们任何一个数本质上都是一个二进制序列,那么同理我们的商本质也是一个由0和1组成的二进制序列,那么我们假设我们的数是8个二进制位,那么如果说我们的商是01000111,那么我们知道我们a=b*商+余数,那么我们可以将我们这个商根据它的二进制序列将我们的原式转换成这样的形式: a = b ∗ ( 2 6 + 2 2 + 2 1 + 2 0 ) + 余 数 a=b*(2^6+2^2+2^1+2^0)+余数 a=b∗(26+22+21+20)+余数
那么我们求这个商的值是多少,我们知道我们的商本质是一个二进制序列,那么我们求商实际上就是可以转换为确定商的二进制序列中哪些位置为1,只要把所有二进制位为1的位置确定,那么商的二进制序列我们就知道了,那么商的值自然也就知道了,那么我们确定我们二进制为1的位置
那么现在的思路就是怎么确定商的二进制序列的1会出现在哪些位置呢,那么我的策略就是先依次确定该二进制序列最左侧的1出现的位置是哪里:
假设被除数是a,除数是b,那么对于一个有符号整形的数来说,最高位是符号位,那么其余剩下是数值位,那么对于一个int类型含有32个二进制位的数来说,那么它的最高位就是第31位,因为第32位是符号位,那么我们就假设该二进制序列中最左侧的1在第31位,那么直到我们a除以b得到商或者余数,也就意味着除数b乘以商是一定等于或者小于我们的被除数的,那么如果满足该位置是最左侧的1的话,那么我们二进制序列的第31位为1的得到数比如c乘以我们的除数b,它一定是小于等于我们的被除数a的,那么如果不满足,也就是说该二进制位为1的数乘以除数b大于了我们的被除数a,那么我们知道该二进制位肯定不可能为1,只能为0,那么最左侧的1肯定不在第31位,那么我们就依次右移讨论,那么直到讨论到二进制位为1的该数c乘以b它小于等于我们的被除数a,那么意味着该最左侧的二进制位为1的位置在该位置,那么我们就记录,然后我们在用被除数减去除数a乘以c,然后这个数作为我们的被除数,因为我们a/b可以写成:
a = b ∗ ( 2 6 + 2 2 + 2 1 + 2 0 ) + 余 数 a=b*(2^6+2^2+2^1+2^0)+余数 a=b∗(26+22+21+20)+余数
也就是
a = b ∗ 2 6 + a ∗ 2 2 + a ∗ 2 1 + a ∗ 2 0 + 余 数 a=b*2^6+a*2^2+a*2^1+a*2^0+余数 a=b∗26+a∗22+a∗21+a∗20+余数
最后转换成:
a − b ∗ 2 6 = a ∗ 2 2 + a ∗ 2 1 + a ∗ 2 0 + 余 数 a-b*2^6=a*2^2+a*2^1+a*2^0+余数 a−b∗26=a∗22+a∗21+a∗20+余数
那么确定剩余的为1的二进制位,那么重复刚才的思路,直到最后被除数如果不为0,那么该被除数就是余数。
代码实现:
#include<iostream>
using namespace std;
void chufa(int& a,int& b)
{int res=0;for(int i=29;i>=0;i--){if(a==0){break;}long long ant=(long long)b*(1<<i);if(a>=ant){res+=(1<<i);a-=ant;}}b=res;return;
}
int main()
{int a=280,b=25;cout<<a<<"/"<<b<<"="<<endl;chufa(a,b);cout<<b<<" "<<a<<endl;return 0;
}
相关文章:

位运算算法篇:位运算实现加减乘除
位运算算法篇:位运算实现加减乘除 那么我们想必对加减乘除这些数学计算并不陌生,但是对于我们的计算机来说,由于机器只能识别二进制的语言,那么我们底层的数据都是以二进制的形式存在,那么我们CPU的计算器的加减乘除运…...

【故障处理】ORA-19849 ORA-19612 0RA-17627 ORA-03114
【故障处理】ADG duplicate 异常中断ORA-19849 ORA-19612 0RA-17627 ORA-03114 Corrupt block 84629 found during reading backup piece 一、概述二、报错信息三、报错原因四、解决方法五、其他类似报错5.1 报错信息 一、概述 部署adg执行duplicate异常中断,RMAN过…...

【MQ】Spring3 中 RabbitMQ 的使用与常见场景
一、初识 MQ 传统的单体架构,分布式架构的同步调用里,无论是方法调用,还是 OpenFeign 难免会有以下问题: 扩展性差(高耦合,需要依赖对应的服务,同样的事件,不断有新需求࿰…...

jupyterLab插件开发
jupyter lab安装、配置: jupyter lab安装、配置教程_容器里装jupyterlab-CSDN博客 『Linux笔记』服务器搭建神器JupyterLab_linux_布衣小张-腾讯云开发者社区 Jupyter Lab | 安装、配置、插件推荐、多用户使用教程-腾讯云开发者社区-腾讯云 jupyterLab插件开发教…...

拯救者Y9000P双系统ubuntu22.04安装4070显卡驱动
拯救者Y9000P双系统ubuntu22.04安装4070显卡驱动 1. 前情: 1TB的硬盘,分了120G作ubuntu22.04。/boot: 300MB, / : 40GB, /home: 75G, 其余作swap area。 2. 一开始按这个教程:对我无效 https://blog.csdn.net/Eric_xkk/article/details/1…...
QT-常见问题
1. C(特别是 Qt)开发中,内存优化的方法 1. 合理管理对象生命周期,使用智能指针 Qt 提供了 QScopedPointer 和 QSharedPointer 来管理对象生命周期,避免手动 delete 导致的内存泄漏。 2. 减少内存占用 QString、QBy…...
如何通过腾讯 ima.copilot 训练自己的知识库
如何通过腾讯 ima.copilot 训练自己的知识库 在信息爆炸的时代,拥有一个专属的知识库,能让我们在学习、工作中快速获取所需信息,极大地提升效率。腾讯推出的 AI 智能工作台 ima.copilot,为我们打造个人知识库提供了便利。今天&am…...
关于近期我的交流之深度思考DeepSeek归纳总结
以下内容我摘自昨天 2025-2-9 群里的讨论,只涉及我的观点内容,会让DeepSeek进行深度思考 抢财猫: 能提出一个好问题不容易的,问题边界包含了所有认知,提问题需要能力的 抢财猫: 每个人都相当于一个大模型,自己给自己投入了多少算力,训练了多少数据参数,自己心里有数…...
智能生鲜配送管理系统:生鲜及快消品行业的数字化转型利器
在生鲜及快消品行业,高效的供应链管理是企业成功的关键。随着科技的不断进步,越来越多的企业开始采用智能化管理软件来提升运营效率、降低成本并优化客户体验。今天,我们就来了解一下这类智能生鲜配送管理系统的核心功能和技术优势࿰…...
DeepSeek和ChatGPT的优劣或者区别(答案来DeepSeek和ChatGPT)
DeepSeek的答案 DeepSeek与ChatGPT作为当前两大主流AI模型,在架构设计、性能表现、应用场景等方面存在显著差异,以下从多个维度进行对比分析: 一、架构与训练效率 架构设计 DeepSeek:采用混合专家(MoE)框架…...

【C语言标准库函数】标准输入输出函数详解[5]:格式化文件输入输出
目录 一、fprintf() 函数 1.1. 函数简介 1.2. fprintf使用场景 1.3. 注意事项 1.4. 示例 二、fscanf() 函数 2.1. 函数简介 2.2. fscanf使用场景 2.3. 注意事项 2.3. 示例 三、总结 在 C 语言中,格式化文件输入输出函数能够让我们以特定的格式对文件进行…...
[概率论] 随机变量
Kolmogorov 定义的随机变量是基于测度论和实变函数的。这是因为随机变量的概念需要精确地定义其可能的取值、发生的概率以及这些事件之间的依赖关系。 测度论:在数学中,测度论是用来研究集合大小的理论,特别是无穷可数集和无界集的大小。对于…...
中国通信企业协会通信网络安全服务能力评定安全设计与集成服务能力评定三级要求准则...
安全设计与集成服务能力三级是通信网络安全服务能力评定安全设计与集成服务能力评定的最高等级,所需的要求也会更加严苛,不仅要满足安全设计与集成服务二级能力要求的所有条款,还要满足以下要求: 规模与资产要求 1)单位正规编制员…...
【C++语言】类和对象(下)
一、再谈构造函数 1.1 构造函数体赋值 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;} private:int _year;int _mont…...

【Spring】什么是Spring?
什么是Spring? Spring是一个开源的轻量级框架,是为了简化企业级开发而设计的。我们通常讲的Spring一般指的是Spring Framework。Spring的核心是控制反转(IoC-Inversion of Control)和面向切面编程(AOP-Aspect-Oriented Programming)。这些功能使得开发者…...
全面理解-c++11中的智能指针
在 C 中,智能指针(Smart Pointers) 是用于自动管理动态分配内存的类模板,遵循 RAII(Resource Acquisition Is Initialization) 原则,确保资源在生命周期结束时被正确释放,避免内存泄…...

【jmeter】在windows中,创建的变量,在jmeter中,读取变量失败的问题,路径问题
1.0 在windows中,jmeter读取变量失败 在路径配置的时候,配置按照D:\FtpDownload\${file_name}运行之后,下载的文件,文件名出现问题 \取消了$符号的意义,所以需要更改路径 D:\\FtpDownload\\${file_name}...

【CubeMX-HAL库】STM32F407—无刷电机学习笔记
目录 简介: 学习资料: 跳转目录: 一、工程创建 二、板载LED 三、用户按键 四、蜂鸣器 1.完整IO控制代码 五、TFT彩屏驱动 六、ADC多通道 1.通道确认 2.CubeMX配置 ①开启对应的ADC通道 ②选择规则组通道 ③开启DMA ④开启ADC…...

使用 POI-TL 和 JFreeChart 动态生成 Word 报告
文章目录 前言一、需求背景二、方案分析三、 POI-TL JFreeChart 实现3.1 Maven 依赖3.3 word模板设置3.2 实现代码 踩坑 前言 在开发过程中,我们经常需要生成包含动态数据和图表的 Word 报告。本文将介绍如何结合 POI-TL 和 JFreeChart,实现动态生成 W…...

xxl-job的分片广播
目录 xxl-job的分片广播 场景引入 xxl-job简介 xxl-job的部署安装 代码编写 1.导入依赖 2.yml文件编写 3.编写xxl-job执行器配置类,维护一个xxl-job执行器的bean 4.编写第一个任务,任务名字叫firstJob 5.进入服务端,增加执行器和任务…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...