加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /)

这篇具有很好参考价值的文章主要介绍了加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /),杂谈,c语言,利用位运算实现加减乘除,位运算

🚀前言

这也是阿辉开的新专栏,知识将会很零散不成体系,不过绝对干货满满,今天这一篇利用位运算实现加减乘除费了阿辉九牛二虎之力,干的很自备饮水😆不多bb,进入今天的学习吧!!!


以下int均为有符号int,所求的加减乘除也是int类型的整型数
严谨 😏

🚀异或运算以及与运算

在写加减乘除之前,先给铁子们介绍一下异或运算以及与运算的其他理解
异或运算:也叫无进位相加
这怎么理解呢?
铁子们都知道,异或运算,是二进制位相异为1,相同为0

其实异或运算也可以解释为无进位相加。这是什么意思呢?就是对应的二进制位相加,如果产生进位就将进位舍去。什么是进位呢?对应的二进制位相加为2时就向前进一位,就像我们小时候学的加法一样。而二进制进位后,本位就剩下一个0。而在异或运算中,进位被舍去,所以当两个位都为1时,异或的结果为0;当两个位都为0时,异或的结果也为0;只有一个位为1,另一个位为0时,异或的结果才为1

给铁子们上图解释:
加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /),杂谈,c语言,利用位运算实现加减乘除,位运算
与运算:得到对应二进制位相加的进位信息右移一位后的结果
怎么理解呢?
铁子们都知道,与运算,是两二进制位都为1为1,一个为0就为0

实际上,与运算是得到对应二进制位相加的进位信息右移一位后的结果,怎么理解呢?二进制位相加时产生的进位保留,没有进位舍去直接置0,然后将得到的结果右移一位。

给铁子们上图解释:

加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /),杂谈,c语言,利用位运算实现加减乘除,位运算

🚀加法的实现

有了上述关于异或运算和与运算的新的理解,基于此,我们可以用他们拼凑出加法,怎么拼呢?铁子们接着看👇

根据前面的讲解我们知道,两个数异或运算得到的是两个数相加去掉进位信息后的结果,而两个数与运算得到的是两个数相加保留进位信息右移一位后的结果,铁子们,睁大眼骚操作来了😏,异或运算是去掉进位信息的结果,与运算结果再左移一位(进位信息右移了嘛,再左移回去)是保留进位信息的结果,那两个数异或运算的结果加上两个数与运算再左移一位的结果不就是两个数相加的结果嘛

铁子们一定会说这不还得在加一遍,有毛用,别急还有更骚的😏😏,两个数的异或运算的结果和与运算左移一位的结果,只要与运算左移一位的结果不为0也就是进位信息不为0,得到的俩个数重复上述操作,直到进位信息为0,异或运算的结果不就是两数相加的结果嘛。铁子们,骚不骚?觉得骚的,记得在评论区给阿辉扣个666 😘

肯定有老铁会说难道进位信息一定会计算到0吗?好问题哈哈哈哈 😜,一定会计算到0,为什么呢?阿辉用图说明,鼠标写的铁子们凑合看一下🙏
加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /),杂谈,c语言,利用位运算实现加减乘除,位运算

异或运算就是把二进制数相加得到如上述不加上蓝色进位信息的结果,然后和进位信息继续异或,直到不在产生进位信息得到相加的结果

如果还有铁子不懂,可以私聊阿辉加个微信,阿辉必给你搞明白,上面进位信息为啥一定会算到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);//减数取相反数后与被减数相加
}

🚀乘法的实现

乘法也是稀松平常,没什么难理解的地方,怎么实现呢?铁子们别急,阿辉整个例子你们就懂了👊
加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /),杂谈,c语言,利用位运算实现加减乘除,位运算
铁子们这里阿辉,先给出代码再解释:

int Mul(int x, int y)//传进来两数
{
	int ret = 0;//作返回值
	for (int i = 0; i <= 30; i = Add(i, 1))//符号位不算,固定循环31次
	{
		//遍历y的除符号位的31位,判断是否为1
		if (y >> i & 1 == 1)//对应二进制位为1,进入if
		{
		//因为二进制位只有0和1,0乘任何数还是0,所以不用管
		//当为1时,1乘任何数还是它本身,所以加上x
			ret = Add(ret, x);
		}
		//遍历一次x左移一位,因为再循环,y向后一位找1
		//这个1代表2的i次方,相当于把这个2的i次方乘在了x上
		//左移一位相当于乘2,右移一位相当于除2
		x <<= 1;
	}
	return ret;
}

铁子们注意,上述实现的乘法可以计算负数,为什么呢?这与原反补码有关,阿辉水平有限解释不出来,铁子们感兴趣可以研究一下,找到记得和阿辉分享一波,嘿嘿
铁子们,没懂的话可以自己想想试试,如果实在不懂可以私信我好吧,下面的除法才是重头戏特别麻烦 💀

🚀除法的实现

除法比较麻烦,这里阿辉实现的除法是整数除法,上图给大家引导:
加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /),杂谈,c语言,利用位运算实现加减乘除,位运算
关于将除法抽象成代码,这是一个相当复杂的过程。首先,让我们回顾一下如何进行除法运算。在我们最常见的十进制系统中,以被除数728÷8为例,我们是如何得出十位上的9的呢?除法运算实际上是在找到一个最大的数,使得将除数8乘以这个数接近被除数728,然后再用728减去这个数720(8×90),然后继续这个过程,要么除尽要么除不尽。不过在整数除法中,如果除不尽就舍去余数。实际上,二进制数的除法运算也是类似的过程。与十进制不同的是,二进制位只有1,因此只需要将除数101后面加上0来逼近被除数101001011(在二进制中,将除数101左移一位就相当于除数后面加上一个0),然后用101001011减去101×1000000(101左移6位),然后重复这个操作直到除尽

但是我们在写代码时不能通过除数左移去逼近,因为这样会有风险
加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /),杂谈,c语言,利用位运算实现加减乘除,位运算
红色方块的位置代表符号位。我们给除数b左移,左移一位比a小,继续左移直到比a大才知道上一次左移逼近被除数a。但是对于我们给的这个例子b怎么左移也不可能比a大,因为当b右移11位时,符号位变成1了,b直接变成负数了。
这里我们通过被除数右移代替除数左移来逼近除数达到一样的效果,因为被除数右移多少位逼近除数也就是除数左移多少位逼近被除数,这样就不会有改变符号位的风险
加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /),杂谈,c语言,利用位运算实现加减乘除,位运算
我们让除数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的结果
加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /),杂谈,c语言,利用位运算实现加减乘除,位运算
就是上面这张图这个原理,没啥难的,不一定要加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);//不含系统最小值的除法
}

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,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);
}

好的,到这里阿辉就讲完了,说实话不容易,着力于构思怎么把铁子们讲懂,应该没有比我更细节的了,写完这篇满满成就感,铁子们觉得讲得不错的话给阿辉评论区扣个666,哈哈哈!!!!
加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /),杂谈,c语言,利用位运算实现加减乘除,位运算文章来源地址https://www.toymoban.com/news/detail-775296.html

到了这里,关于加减乘除简单吗?不,一点都不,利用位运算实现加减乘除(代码中不含+ - * /)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 不用加减乘除做加法

    写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号,本题OJ链接 数据范围:两个数都满足 −10≤n≤1000 进阶:空间复杂度 O(1),时间复杂度 O(1) 分析 :本题要求不能使用+、-、*、/,所以我们应该从二进制的角度去考虑,因为二进制的加法可以通

    2024年02月10日
    浏览(29)
  • C语言加减乘除运算

    加减乘除是常见的数学运算,C语言当然支持,不过,C语言中的运算符号与数学中的略有不同,请见下表。 加法 减法 乘法 除法 求余数(取余) 数学 + - × ÷ 无 C语言 + - * / % C语言中的加号、减号与数学中的一样,乘号、除号不同;另外C语言还多了一个求余数的运算符,就是

    2024年02月06日
    浏览(30)
  • 图像四则运算(加减乘除)

    实验目的: 1.了解图像的算术运算在数字图像处理中的初步应用。 2.体会图像算术运算处理的过程和处理前后图像的变化。 3.能够实现简单的图像处理 实验原理: 图像的代数运算包括加,减,乘,除,这些运算的主要对象是图像数据块中的数据。这四种代数运算可以由如

    2024年02月08日
    浏览(38)
  • 只使用位运算实现加减乘除

    在线OJ: LeetCode 29. 两数相除 原题目的要求是不能使用乘法, 除法和取余运算符实现除法. 在本篇博客中把题目要求提高一点, 这里只使用位运算来实现, 顺便的也就把只使用位运算实现加减乘除实现了. 首先我们需要知道两数之和可以是两个数位相加和不进位相加之和, 而两数进

    2024年02月06日
    浏览(36)
  • Rust 复数运算,重载加减乘除运算

    复数定义 由实数部分和虚数部分所组成的数,形如a+bi 。 其中a、b为实数,i 为“虚数单位”,i² = -1,即虚数单位的平方等于-1。 a、b分别叫做复数a+bi的实部和虚部。 当b=0时,a+bi=a 为实数; 当b≠0时,a+bi 又称虚数; 当b≠0、a=0时,bi 称为纯虚数。 实数和虚数都是复

    2024年02月13日
    浏览(28)
  • Pytorch入门:Tensor加减乘除矩阵运算

    若张量维数大于2,则对最后两维进行matmul。进行此运算的要求是张量a与b除最后两维外的其他维必须一致:

    2024年02月12日
    浏览(31)
  • Python Opencv实践 - 图像的加减乘除

               

    2024年02月13日
    浏览(25)
  • Java进行数字计算 BigDecimal计算(加减乘除)

    Double只能处理16位有效数精度,在某些情况下精度对其需求是不够的,所以就有了BigDecimal。因为BigDecimal的精度范围的范围大,所以在问我们的开发业务中对精度要求高的属性,就需要BigDecimal来进行存储计算,防止精度丢失。这里我主要介绍一下BigDecimal的加,减,乘,除。四

    2023年04月08日
    浏览(29)
  • SpringBoot ES 聚合后多字段加减乘除

    在SpringData Elasticsearch中,聚合统计的原理主要依赖于Elasticsearch本身的聚合框架。Elasticsearch提供了强大的聚合功能,使得你可以对文档进行各种计算和统计,从而得到有关数据集的有用信息。 Elasticsearch的聚合(Aggregation)是一种强大的数据分析和统计工具,它允许你对文档集

    2024年01月21日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包