博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[转]C++二进制完成加减乘除
阅读量:2230 次
发布时间:2019-05-09

本文共 3093 字,大约阅读时间需要 10 分钟。

 

首先介绍计算机的二进制码

二进制常用的有原码,反码和补码,他们都是由最左边的一个符号位和右边的数值位构成。在计算机中为了更低成本的计算,数据都是用补码来存储和运算的。

原码

最高位表示符号位(0代表正数,1代表负数)。剩下的位数,是这个数的绝对值的二进制。

比如 一个int变量大小为4字节,在32位的编译器中的二进制表示就是00000000 00000000 00000000 00000000

那么10 的原码就是00000000 00000000 00000000 00001010
−10的原码就是 10000000 00000000 00000000 00001010

反码

正数的反码和其原码是一样的

负数的反码就是在其原码的基础上 符号位不变 其他位取反。

10的反码就是00000000 00000000 00000000 00001010 和原码一样

−10的反码就是11111111 11111111 11111111 11110101

补码

正数的补码就是其原码

负数的补码就是在其反码的基础上+1

10的补码就是00000000 00000000 00000000 00001010

−10的补码就是 11111111 11111111 11111111 11111011

总结一下

计算机系统中,数值一律用补码来表示:因为补码可以使符号位和数值位统一处理,同时可以使减法按照加法来处理。

二进制编码:数值编码分为原码,反码,补码,符号位均为0正1负。

原码 -> 补码: 数值位取反加1

补码 -> 原码: 对该补码的数值位继续 取反加1

补码 的绝对值(称为真值):正数的真值就是本身,负数的真值是各位(包括符号位)取反加1(即变成原码并把符号位取反).

介绍基本的位操作

^:按位异或;&:按位与; | :按位或

b -> -b : 各位(包括符号位)取反加1

用位操作实现加法运算

我们先不考虑进位,在1位数的加法上,如下

1. 1+1=0
2. 1+0=1
3. 0+1=1
4. 0+0=0
很明显上面几个表达式我们可以用异或进行统一
1. 1^1=0
2. 1^0=1
3. 0^1=1
4. 0^0=0
这样我们就完成了最基础的一位数的加法,但是怎么计算二位以上的加法呢?我们发现现在方法的问题在于不能够获取进位,于是我们通过观察一位数的加法的式子,发现只有两个数位都是1的时候才会有进位,其他都不进位,这不是和&很像吗? 我们通过把+换成&得到下式
1. 1&1=0 不进位
2. 1&0=1 不进位
3. 0&1=1 不进位
4. 0&0=0 进位
那么我们把所有位进行&操作,然后<<左移一位,不就可以当作加数当前的进位吗?
到这里我们就完整解决了二进制相加问题中,对应位的相加和进位的问题
1. x^y 加法
2. (x&y)<<1 进位操作
那么总结一下:

定理1:设a,b为两个二进制数,则a+b=a^b+(a&b)<<1。
证明:

a^b是不考虑进位时加法结果。当二进制位同时为1时,才有进位,因此 (a&b)<<1是进位产生的值,称为进位补偿。将两者相加便是完整加法结果。

定理2:使用定理1可以实现只用位运算进行加法运算。
证明:

利用定理1中的等式不停对自身进行迭代。每迭代一次,进位补偿右边就多一位0,因此最多需要加数二进制位长度次迭代,进位补偿就变为0,这时运算结束。

那么我们可以根据上面的定理得到实际的C++代码如下

int add(int a, int b){    int re = a;    while(b){        int tmp = a;        a = a^b;        b = (tmp&b)<<1;        re = a;    }    return re;}

用位操作实现减法

减法和加法原理相同,减去一个数相当于加上这个数的相反数,所以完全可以利用加法操作,唯一需要做的就是求出被减数的相反数。

求相反数的方法:每一位取反,末位加一。
代码如下:

int subtraction(int a, int b){    b = add(~b,1); // 求b的相反数    return add(a, b);}

用位操作实现乘法

二进制的乘法同十进制的乘法并无什么不一样,对于a∗b每次只需要将a左移对应的位,然后同上一次的结果相加即可

当b的对应位为1的时候,对a左移一位相加即可
当b的对应位位0的时候,对a左移一位,但是不相加
注意到我们上面的操作都是不包括符号位的,因此我们单独考虑符号位。
代码如下

int getSign(int n){    unsigned count = 0;    //计算n的位数    do{        ++count;    }while(n >> count)    //得到n的最左边的位    return n >> (count-1);}int mul(int a, int b){    bool isNegative = false;    if(getSign(a) ^ getSigned(b))        isNegative = true;    if(a < 0) a = add(~a,1);//求出a的绝对值    if(b < 0) b = add(~b,1);//求出b的绝对值    int res = 0;    while(b){               //当b不为0,继续循环        if(b & 1)           //当b当前位为1 才需要加a            res = add(res,a);        a = a << 1;        b = b >> 1;    }    if(isNegative)        add(~res,1);    return res;}

二进制除法

同乘法一样,除法一样可以用减法来代替,当a≥b才可以上商,在每次上一个商(也就是商值加1)之后,a=a−b

代码如下

int divide(int a, int b){    if(!b)        throw std::runtime_error("Divided can't be zero...");    bool isNegative = false;    bool isNegtive = false;    if(getSign(a) ^ getSign(b))        isNegtive = true;    if(a < 0) a = add(~a,1);//求出a的绝对值    if(b < 0) b = add(~b,1);//求出b的绝对值    int res = 0;    while(a >= b){        res = add(res,1);        a = subtraction(a,b);    }    if(isNegative)        add(~res,1);    return res;}

---------------------
作者:harry_128
来源:CSDN
原文:https://blog.csdn.net/harry_128/article/details/80150502
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:

你可能感兴趣的文章
搞懂分布式技术20:消息队列因何而生
查看>>
搞懂分布式技术21:浅谈分布式消息技术 Kafka
查看>>
后端技术杂谈1:搜索引擎基础倒排索引
查看>>
后端技术杂谈2:搜索引擎工作原理
查看>>
后端技术杂谈3:Lucene基础原理与实践
查看>>
后端技术杂谈4:Elasticsearch与solr入门实践
查看>>
后端技术杂谈5:云计算的前世今生
查看>>
后端技术杂谈6:白话虚拟化技术
查看>>
后端技术杂谈7:OpenStack的基石KVM
查看>>
后端技术杂谈8:OpenStack架构设计
查看>>
后端技术杂谈9:先搞懂Docker核心概念吧
查看>>
后端技术杂谈10:Docker 核心技术与实现原理
查看>>
夯实Java基础系列2:Java自动拆装箱里隐藏的秘密
查看>>
夯实Java基础系列1:Java面向对象三大特性(基础篇)
查看>>
夯实Java基础系列3:一文搞懂String常见面试题,从基础到实战,更有原理分析和源码解析!
查看>>
夯实Java基础系列4:一文了解final关键字的特性、使用方法,以及实现原理
查看>>
Java 未来行情到底如何,来看看各界人士是怎么说的
查看>>
IntelliJ 平台 2020 年路线图
查看>>
走进JavaWeb技术世界8:浅析Tomcat9请求处理流程与启动部署过程
查看>>
微软宣布加入 OpenJDK,打不过就改变 Java 未来!
查看>>