前言
对于计算器,可能很多同学都会写,毕竟那是最基础的东西,那今天我来介绍个不那么基础的计算器。记住不要出现+ - * /,我们的宗旨是,不用这些运算符也做出计算器!
一、位运算是什么
1、定义
从现代计算机中所有的数据二进制的形式存储在设备中。即 0、1 两种状态,计算机对二进制数据进行的运算(+、-、*、/)都是叫位运算。
2、运算符
位运算的运算符是不是和我们小学学过的(+、-、*、/)一样呢,那我们来想想,如果说2*2,在二进制里面是0010*0010,我们小学学过的*在这里怎么用用呢,难不成直接霸王硬上弓,直接等于10*10=100,0100=4,刚刚好,perfect !!但是它和我们普通计算器有什么区别呢,二进制运算在于高效。
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
3、乘法
老规矩,回想一下我们小学,甚至是现在,是计算乘法的,列树式(可能口语化了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; }
写得不好,请多指教。