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

计算器——位运算(c语言)

目录

前言

一、位运算是什么

二、实现位运算计算器

1.加法

2、减法

三、乘法

4、除法

总结


前言

对于计算器,可能很多同学都会写,毕竟那是最基础的东西,那今天我来介绍个不那么基础的计算器。记住不要出现+ - * /,我们的宗旨是,不用这些运算符也做出计算器!


一、位运算是什么

1、定义

从现代计算机中所有的数据二进制的形式存储在设备中。即 0、1 两种状态,计算机对二进制数据进行的运算(+、-、*、/)都是叫位运算。

2、运算符

  位运算的运算符是不是和我们小学学过的(+、-、*、/)一样呢,那我们来想想,如果说2*2,在二进制里面是0010*0010,我们小学学过的*在这里怎么用用呢,难不成直接霸王硬上弓,直接等于10*10=100,0100=4,刚刚好,perfect !!但是它和我们普通计算器有什么区别呢,二进制运算在于高效。

名称符号运算规则(例子)
&两个1才为1,其余为0        1001&1111==1001
|有1为1                                1001|1111==1111
异或^一样为0,不一样为1           1001^1111==0110        
取反~0变1,1变0                        ~1001==0110
左移<<左移一位,高位丢,低位补0(相当于*2) 0111<<1   ==  1110
右移  >>右移一位,低位丢,高位补0(相当于/2)1110>>1  ==0111

3、为什么我们要用位运算

位运算可以帮助我们理解计算机的底层原理,而且位运算更高效。


二、实现位运算计算器

1.加法

计算原理:

我们再来看一下二进制运算的简单计算:先来看一下简单的二进制计算原理:

加法:1 + 1 = 0              ——>    异或: 1 ^ 1 = 0

           1 + 0 = 1              ——>                1 ^ 0=1

           0 + 1 = 1              ——>                 0 ^ 1=1

           0 + 0 = 0              ——>                 0 ^ 0=0

是不是发现位运算符可以替代+呢!!

但是两位数的加法它还可以实现对等吗,很明显是不可以的,大家可以自己演算一下。

技术难点在哪里,异或运算不会进位!!不会进位!!不会进位!!

二进制的进位是满2进一,但是为什么异或没有脑子啊,两个1就直接消掉了,不会进1。别着急,它没有脑子,我们有(应该有吧)。

这时候就要用到我们的 与运算

1&1=1  (进位)    

1&0=0 (不进位)

0&0=0  (不进位)

可能有小伙伴疑惑了,有个1,一个0怎么进位啊。

别忘了,我们还有左移(<<)符号

(a&b)<<1  不就相当于进位了吗

那把进位+原来的(a^b)不就得出了下一层数,如果有进位就回头继续得出进位继续加。(这一部分涉及递归,也是加法的核心部分,我尽量写明白一些)

画图如下:

 核心思想:先异或,然后用与,进制加异或之后的数,如果进制不为0,那就一直重复。

代码如下(示例):

int add(int a, int b) {			//加法
	int res = 0;
	int x = a ^ b;               //加法
	int y = a & b;                //进制
	while (y != 0) {              //进制不为0的时候,我们就继续
		y = y << 1;        //左移一位,进位
		res = x ^ y;        //进位加上异或后的值
		int temp = x;       //储存异或之后的值
		x = x ^ y;            //再次异或
		y = temp & y;            //因为上一次进制不为0,进入了循环,再次求进制,如果不为0,就继                                            
	}                                续,直到进制为0,完成运算
	return x;
}

2、减法

这里我们先来了解一个概念:二进制取反

这里不在赘述,补码,反码,源码的原理,直接上结论。

一个二进制数,取反之后+1就是它的相反数。

也就是说 a的相反数是~a+1

那我们在小学的时候学过,减法是不是一个被减数减一个减数,直观来看就是a-b,我们初中又学过负数,那我们是不是把a-b=a+(-b)呢。理解了这个,减法就很简单了。

int subtraction(int a, int b) {			//减法
	int res = 0;
	res=add(a, add(~b, 1));            
	return res;
}

记得不要直接写~b+1,我们是在做位运算加减乘除,用了+号,它就不干净了!!QAQ


三、乘法

老规矩,回想一下我们小学,甚至是现在,是计算乘法的,列树式(可能口语化了hh)

图片如下:

 

 很显然,和我们以前学过的是一模一样的,但是我们要用代码来运算就有一点点困难了。

分析:

开始,第二乘数的最低位*第一乘数,乘完一次,然后就是第二乘数的次低位*第一乘数,这样慢慢推。

那么因为二进制数里面的每一位是只有0或者1的,如果第二乘数的最低那位是0,等于没有算,1就等于保留第一乘数本身。

再有,我们要注意它的移动,做乘法树式的时候,你会发现我圈出来那一部分,它的开始的地方是不一样的,如果补满0,他们的大小就不会都是第一乘数本身,二是第一乘数本身的2^n

最后一点,我们要判断它的正负性,大家都会,可直接看代码

代码如下:

int inverse(int a) {		//找相反数
	add(~a, 1);
	return a;
}




int multiplication(int a, int b) {					//乘法
	int n = 0;
	if (a < 0) {
		a = inverse(a);        //判断第一乘数
		n++;
	}
	if (b < 0) {                //判断第二乘数
		b = inverse(b);
		n++;	
	}
	int res = 0;            //结果值
	while (b != 0) {         
		if ((b & 1) != 0) {           //保留个位数,什么数&1,都保留最低位,     
			res = add(res, a);        //保留a进结果值里面
		}
			b = b >> 1;                //每次b(第二乘数)都要向右走一位,也就是最低位向左
			a=a << 1;                    //第一乘数先左走一位,因为下面相加的时候,图里有
	}
	if (n == 1) 
		res = inverse(res);            //判断正负
	return res;
}


4、除法

实际上是乘法的逆向过程,用文字很难演示了,能力有限,让我水过去吧

int inverse(int a) {		//找相反数
	add(~a, 1);
	return a;
}



int division(int a, int b) {			//除法
	int n = 0;
	if (a < 0) {
		a = inverse(a);
		n++;
	}
	if (b < 0) {
		b = inverse(b);
		n++;
	}
	int res = 0;
	for (int i = 30; i >= 0; i = subtraction(i, 1)) {
		if ((a >> i) >= b) {
			res = res | (1 << i);
			a = subtraction(a, b << i);
		}
	}
	if (n == 1)
		res = inverse(res);
	return res;

总结

完整代码如下:

#include<stdio.h>

int inverse(int a) {		//找相反数
	add(~a, 1);
	return a;
}

int add(int a, int b) {			//加法
	int res = 0;
	int x = a ^ b;
	int y = a & b;
	while (y != 0) {
		y = y << 1;
		res = x ^ y;
		int temp = x;
		x = x ^ y;
		y = temp & y;
	}
	return x;
}

int subtraction(int a, int b) {			//减法
	int res = 0;
	res=add(a, add(~b, 1));
	return res;
}

int multiplication(int a, int b) {					//乘法
	int n = 0;
	if (a < 0) {
		a = inverse(a);
		n++;
	}
	if (b < 0) {
		b = inverse(b);
		n++;	
	}
	int res = 0;
	while (b != 0) {
		if ((b & 1) != 0) {
			res = add(res, a);
		}
			b = b >> 1;
			a=a << 1;
	}
	if (n == 1) 
		res = inverse(res);
	return res;
}

int division(int a, int b) {			//除法
	int n = 0;
	if (a < 0) {
		a = inverse(a);
		n++;
	}
	if (b < 0) {
		b = inverse(b);
		n++;
	}
	int res = 0;
	for (int i = 30; i >= 0; i = subtraction(i, 1)) {
		if ((a >> i) >= b) {
			res = res | (1 << i);
			a = subtraction(a, b << i);
		}
	}
	if (n == 1)
		res = inverse(res);
	return res;
}


int main() {
	int a = 0, b = 0,res=0;
	char h = '0';
	while (h != '@') {
		printf("这是一个简易的计算器!请输入您要进行的操作(+,-,*,/),输入@退出程序:\n");
		printf("请输入你要计算的式子(整数),大小不能超过1000000000\n");
		scanf_s("%d%c%d", &a, &h, 1, &b);
		switch (h) {
		case '+':
			res=add(a, b);
			break;
		case '-':
			res = subtraction(a, b);
			break;
		case '*':
			res = multiplication(a, b);
			break;
		case '/':
			res = division(a, b);
			break;
		}
		printf("%d\n", res);
	}
	return 0;
}

写得不好,请多指教。

相关文章:

  • Maven 基础 5 第一个Maven 项目(IDEA 生成)
  • TypeScript算法题实战——哈希表篇
  • 嵌入式分享合集71
  • 又一巅峰神作 14年工作经验大佬手写“微服务项目下高并发的流量治理”,太牛了
  • 谷歌翻译失败解决方案
  • 网络安全面试题目及详解
  • 【初阶与进阶C++详解】第二十三篇:异常(异常抛出+异常捕获+异常优缺点)
  • 第二弹:看了图像分割一系列Unet、DeepLabv3+改进期刊论文,总结改进创新的一些相同点
  • HTML期末大作业 使用HTML+CSS制作科技文化主题网站(航天之路)
  • ElasticSearch_04_批量处理命令mget和bulk的使用
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • web期末作业设计网页 html+css+js制作非物质文化遗产坝漆国漆 2页
  • 基于差异演化的寄生樽海鞘群算法
  • 神经网络过拟合什么意思,神经网络过拟合现象
  • python+selenium的web自动化测试之8种元素定位方式详解
  • 3.7、@ResponseBody 和 @RestController
  • Android优雅地处理按钮重复点击
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • ECMAScript入门(七)--Module语法
  • es6--symbol
  • HTTP中的ETag在移动客户端的应用
  • JS数组方法汇总
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • Redis 懒删除(lazy free)简史
  • ViewService——一种保证客户端与服务端同步的方法
  • Xmanager 远程桌面 CentOS 7
  • 闭包--闭包作用之保存(一)
  • 给第三方使用接口的 URL 签名实现
  • 构建二叉树进行数值数组的去重及优化
  • 怎样选择前端框架
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • #mysql 8.0 踩坑日记
  • #pragma multi_compile #pragma shader_feature
  • #stm32整理(一)flash读写
  • $.ajax,axios,fetch三种ajax请求的区别
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (译) 函数式 JS #1:简介
  • (转)h264中avc和flv数据的解析
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • .NET NPOI导出Excel详解
  • .net 流——流的类型体系简单介绍
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .net(C#)中String.Format如何使用
  • .net下简单快捷的数值高低位切换
  • @EnableWebMvc介绍和使用详细demo
  • @value 静态变量_Python彻底搞懂:变量、对象、赋值、引用、拷贝
  • @我的前任是个极品 微博分析
  • [ vulhub漏洞复现篇 ] Grafana任意文件读取漏洞CVE-2021-43798
  • [ 手记 ] 关于tomcat开机启动设置问题