当前位置: 首页 > news >正文

位运算算法篇:位运算实现加减乘除

位运算算法篇:位运算实现加减乘除

那么我们想必对加减乘除这些数学计算并不陌生,但是对于我们的计算机来说,由于机器只能识别二进制的语言,那么我们底层的数据都是以二进制的形式存在,那么我们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=b26+a22+a21+a20+
最后转换成:
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+余数 ab26=a22+a21+a20+
那么确定剩余的为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;	
}

在这里插入图片描述

相关文章:

位运算算法篇:位运算实现加减乘除

位运算算法篇&#xff1a;位运算实现加减乘除 那么我们想必对加减乘除这些数学计算并不陌生&#xff0c;但是对于我们的计算机来说&#xff0c;由于机器只能识别二进制的语言&#xff0c;那么我们底层的数据都是以二进制的形式存在&#xff0c;那么我们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异常中断&#xff0c;RMAN过…...

【MQ】Spring3 中 RabbitMQ 的使用与常见场景

一、初识 MQ 传统的单体架构&#xff0c;分布式架构的同步调用里&#xff0c;无论是方法调用&#xff0c;还是 OpenFeign 难免会有以下问题&#xff1a; 扩展性差&#xff08;高耦合&#xff0c;需要依赖对应的服务&#xff0c;同样的事件&#xff0c;不断有新需求&#xff0…...

jupyterLab插件开发

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

拯救者Y9000P双系统ubuntu22.04安装4070显卡驱动

拯救者Y9000P双系统ubuntu22.04安装4070显卡驱动 1. 前情&#xff1a; 1TB的硬盘&#xff0c;分了120G作ubuntu22.04。/boot: 300MB, / : 40GB, /home: 75G, 其余作swap area。 2. 一开始按这个教程&#xff1a;对我无效 https://blog.csdn.net/Eric_xkk/article/details/1…...

QT-常见问题

1. C&#xff08;特别是 Qt&#xff09;开发中&#xff0c;内存优化的方法 1. 合理管理对象生命周期&#xff0c;使用智能指针 Qt 提供了 QScopedPointer 和 QSharedPointer 来管理对象生命周期&#xff0c;避免手动 delete 导致的内存泄漏。 2. 减少内存占用 QString、QBy…...

如何通过腾讯 ima.copilot 训练自己的知识库

如何通过腾讯 ima.copilot 训练自己的知识库 在信息爆炸的时代&#xff0c;拥有一个专属的知识库&#xff0c;能让我们在学习、工作中快速获取所需信息&#xff0c;极大地提升效率。腾讯推出的 AI 智能工作台 ima.copilot&#xff0c;为我们打造个人知识库提供了便利。今天&am…...

关于近期我的交流之深度思考DeepSeek归纳总结

以下内容我摘自昨天 2025-2-9 群里的讨论,只涉及我的观点内容,会让DeepSeek进行深度思考 抢财猫: 能提出一个好问题不容易的,问题边界包含了所有认知,提问题需要能力的 抢财猫: 每个人都相当于一个大模型,自己给自己投入了多少算力,训练了多少数据参数,自己心里有数…...

智能生鲜配送管理系统:生鲜及快消品行业的数字化转型利器

在生鲜及快消品行业&#xff0c;高效的供应链管理是企业成功的关键。随着科技的不断进步&#xff0c;越来越多的企业开始采用智能化管理软件来提升运营效率、降低成本并优化客户体验。今天&#xff0c;我们就来了解一下这类智能生鲜配送管理系统的核心功能和技术优势&#xff0…...

DeepSeek和ChatGPT的优劣或者区别(答案来DeepSeek和ChatGPT)

DeepSeek的答案 DeepSeek与ChatGPT作为当前两大主流AI模型&#xff0c;在架构设计、性能表现、应用场景等方面存在显著差异&#xff0c;以下从多个维度进行对比分析&#xff1a; 一、架构与训练效率 架构设计 DeepSeek&#xff1a;采用混合专家&#xff08;MoE&#xff09;框架…...

【C语言标准库函数】标准输入输出函数详解[5]:格式化文件输入输出

目录 一、fprintf() 函数 1.1. 函数简介 1.2. fprintf使用场景 1.3. 注意事项 1.4. 示例 二、fscanf() 函数 2.1. 函数简介 2.2. fscanf使用场景 2.3. 注意事项 2.3. 示例 三、总结 在 C 语言中&#xff0c;格式化文件输入输出函数能够让我们以特定的格式对文件进行…...

[概率论] 随机变量

Kolmogorov 定义的随机变量是基于测度论和实变函数的。这是因为随机变量的概念需要精确地定义其可能的取值、发生的概率以及这些事件之间的依赖关系。 测度论&#xff1a;在数学中&#xff0c;测度论是用来研究集合大小的理论&#xff0c;特别是无穷可数集和无界集的大小。对于…...

中国通信企业协会通信网络安全服务能力评定安全设计与集成服务能力评定三级要求准则...

安全设计与集成服务能力三级是通信网络安全服务能力评定安全设计与集成服务能力评定的最高等级&#xff0c;所需的要求也会更加严苛&#xff0c;不仅要满足安全设计与集成服务二级能力要求的所有条款&#xff0c;还要满足以下要求&#xff1a; 规模与资产要求 1)单位正规编制员…...

【C++语言】类和对象(下)

一、再谈构造函数 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;} private:int _year;int _mont…...

【Spring】什么是Spring?

什么是Spring&#xff1f; Spring是一个开源的轻量级框架&#xff0c;是为了简化企业级开发而设计的。我们通常讲的Spring一般指的是Spring Framework。Spring的核心是控制反转(IoC-Inversion of Control)和面向切面编程(AOP-Aspect-Oriented Programming)。这些功能使得开发者…...

全面理解-c++11中的智能指针

在 C 中&#xff0c;智能指针&#xff08;Smart Pointers&#xff09; 是用于自动管理动态分配内存的类模板&#xff0c;遵循 RAII&#xff08;Resource Acquisition Is Initialization&#xff09; 原则&#xff0c;确保资源在生命周期结束时被正确释放&#xff0c;避免内存泄…...

【jmeter】在windows中,创建的变量,在jmeter中,读取变量失败的问题,路径问题

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

【CubeMX-HAL库】STM32F407—无刷电机学习笔记

目录 简介&#xff1a; 学习资料&#xff1a; 跳转目录&#xff1a; 一、工程创建 二、板载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 实现代码 踩坑 前言 在开发过程中&#xff0c;我们经常需要生成包含动态数据和图表的 Word 报告。本文将介绍如何结合 POI-TL 和 JFreeChart&#xff0c;实现动态生成 W…...

xxl-job的分片广播

目录 xxl-job的分片广播 场景引入 xxl-job简介 xxl-job的部署安装 代码编写 1.导入依赖 2.yml文件编写 3.编写xxl-job执行器配置类&#xff0c;维护一个xxl-job执行器的bean 4.编写第一个任务&#xff0c;任务名字叫firstJob 5.进入服务端&#xff0c;增加执行器和任务…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...