目录

一、前言

一、原码、反码、补码定义

1. 原码

2. 反码

3. 补码

二、补码的加减计算


一、前言

首先,要知道计算机底层都以二进制表示,数字也不例外,那么,如果数字加减运算的话,要怎么算呢?

在计算机内部只有加法器,既产生数的和的装置,在电子学中,加法器为一种数位电路,可进行加法运算。

对于十以内的加法运算,有相关的4个量:

  1. 被加数 A
  2. 加数 B
  3. 数字相加之和 S
  4. 相加之后的进位 C

为什么考虑进位呢?要想想计算机不是人,他要按部就班的进行计算,我们初学加法不也是进行进位运算嘛,所以这一位记录是整个运算算法必要的数据。

十进制我们了解了,那么二进制有什么不一样呢?

二进制只有0和1,1+1就会产生进位,如果我们只考虑1+1的话,用上面四个数据就可以了,但是,只有1+1能做什么呢?变成两位怎么算?那每一位要怎么做?

比如11 + 01(从右向左一次为第一位和第二位)第一位相加要进位,那么对于第二位来说是不是要处理第一位产生的进位呢!这时假设第二位是你要操作的位,就要比第一位多出一个要处理的数据。

所以,对于二进制普遍处理而言,二进制加法要处理5个量:

  1. 被加数 A
  2. 加数 B
  3. 前一位的进位 CIN
  4. 数字相加之和 S
  5. 相加之后的进位 COUT

要实现32位的二进制加法,一种自然的想法就是将1位的二进制加法重复32次(即逐位进位加法器)。这样做无疑是可行且易行的,但由于每一位的CIN都是由前一位的COUT提供的,所以第2位必须在第1位计算出结果后,才能开始计算;第3位必须在第2位计算出结果后,才能开始计算,等等。而最后的第32位必须在前31位全部计算出结果后,才能开始计算。这样的方法,使得实现32位的二进制加法所需的时间是实现1位的二进制加法的时间的32倍,并且形成的电路是不规则的,并且需要长线驱动,需要大驱动信号和大扇入门。当位数较多时,这种实现方式不太现实。后续的优化算法大家可以自行了解,这里不多说。

总的来说,加法器相对于加减器好实现,但是十位数转换成二进制进行减法计算就产生问题。这是就出现了原码、反码、补码,以便全部用加法计算。

一、原码、反码、补码定义

1. 原码

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:

[+1]原 = 0000 0001

[-1]原 = 1000 0001

第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:

[1111 1111 , 0111 1111]

[-127 , 127]

原码是人脑最容易理解和计算的表示方式.

2. 反码

反码的表示方法是:

正数的反码是其本身

负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

[+1] = [00000001]原 = [00000001]反

[-1] = [10000001]原 = [11111110]反

可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.

3. 补码

补码的表示方法是:

正数的补码就是其本身

负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

[+1] = [00000001]原 = [00000001]反 = [00000001]补

[-1] = [10000001]原 = [11111110]反 = [11111111]补

对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值.

二、补码的加减计算

首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了。

于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:

计算十进制的表达式: 1-1=0

1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2

如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数。

为了解决原码做减法的问题, 出现了反码:

计算十进制的表达式: 1-1=0

1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0

发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0。

于是补码的出现, 解决了0的符号以及两个编码的问题:

1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原

这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:

(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补

-1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000]补 就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的)

使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].

因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.

首先以十进制5+6为例:(这里用八位二进制数表示)

已知,正数的原码、反码、补码相等。

十进制        二进制原码        二进制反码        二进制补码

5        -->  0000 0101        0000 0101        0000 0101

6        -->  0000 0110        0000 0110         0000 0110

5+6 = 11       -->                                                     0000 1011

转换为十进制            11

减法运算(注:减法运算要把加法变为加法并用补码运算,运算完成后转换为原码)

-6-5 ==> -6+(-5)

十进制                二进制原码      二进制反码       二进制补码

-6                 -->  1000 0110          1111 1001        1111 1010

-5                 -->  1000 0101          1111 1010        1111 1011

-6-5 = -11              -->                                                     1111 0101

补码减一                反码                原码

1111 0101 (-1)     1111 0100       1000 1011

转换为十进制                    -11

三、数值类型运算越界问题

1.数值类型所占字节

基本类型

字符类型

'a'-->字符 "ab"-->字符串 '\n'-->特殊字符

char 1字节

unsigned char 无符号字符类型

signed char 有符号字符类型

整型

短整型 short 2字节(存储最大的数65535)

unsigned signed

整型 int 4字节(65535*65535)(与操作系统有关16位为16bit,32位为32bit)

长整型 long 4字节

长长整型 long long 8字节

实型(浮点类型)

单精度浮点类型

float 4字节

双精度浮点类型

double 8字节

多精度浮点类型

long double 12字节

枚举类型

enum()

由上述整理可知,同一种无符号数据类型和有符号数据类型可写入的数据长度相同,但数据范围不同,那么以int(32bit)为例,无符号数越界后会怎样呢?

首先,int类型可以存储4字节(1byte字节 = 8bit ),也就是说可以存储4*8bit(位)的0或1,那么对于无符号数来说,存储的最大数为1111 1111 1111 1111 1111 1111 1111 1111 ,最小数为0000 0000 0000 0000 0000 0000 0000 0000

1111 1111 1111 1111 1111 1111 1111 1111 + 1 结果如下:

1 0000 0000 0000 0000 0000 0000 0000 0000

已知int类型会事先开辟32位来存储0、1,而超过的数字会被省略掉,那到底是左面的数字被“挤”出去了,还是右面的数字被“挤”出去了呢?

这就需要引入一个新概念,大端系统和小端系统。

大端存储模式,是指数据的低位保存在内存的高地址之中,而数据的高位保存在内存的低地址之中,小端的存储模式自然就是,数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。


int main()
{int i = 1;int ret = i >> 8;if (ret== 0)printf("小端存储");elseprintf("大端存储");return 0;
}

如果1是小端存储的话那么0000 0001 这最后一个1就在低地址位,在右移的时候就会丢失,那么补全0之后得出来的数字就是0.如果要是大端存储的话1000 0000  这个1就在最前边,如果你右移8位的时候就不会丢失这个1,得出来就不是1。所以能判段是大端还是小端。

测试后,ubunt系统为小端存储,那么他就是从右向左,依次存储代码,也就是说当他定义的位不够时,最左面超额的部分就会被省略,那么越界后的数值在转换时不会再“合理”,好了,回到上一个问题,32位1的数值+1后的结果到底为什么,现在已经很清楚了吧,为0。

1 0000 0000 0000 0000 0000 0000 0000 0000        1被挤出int范围后面的为产生数值

继续+1

1 0000 0000 0000 0000 0000 0000 0000 0001        结果为1

1 0000 0000 0000 0000 0000 0000 0000 0010        结果为2

...                                                                                ...

1 1111 1111 1111 1111 1111 1111 1111 1111                结果为*********

到这里你会发现,这些数值在int类型中形成了一个循环,当最大值+1时,会变回最小值,后续的计算会变得不准确,这也就是众多越界的一种越界,所以定义时一定记得检查定义的类型。

那现在无符号int已知晓,那么有符号int类型是否一样呢?

我们这里还是以最大值举例(因为最高位为符号位,符号位为1,代表负数)

0111 1111 1111 1111 1111 1111 1111 1111                原码

0111 1111 1111 1111 1111 1111 1111 1111 +1

0111 1111 1111 1111 1111 1111 1111 1111               反码

0111 1111 1111 1111 1111 1111 1111 1111              补码

1000 0000 0000 0000 0000 0000 0000 0000          利用补码与1运算(+1)

两个正数进行+运算时 结果的原码、补码、反码按照正数计算,当结果生成时,若原本的符号位因此发生改变,则产生越界。

1000 0000 0000 0000 0000 0000 0000 0000              结果

(有符号int最高位为1时,他发生越界后结果正常为-0,但是对我们人来说,+0、-0并没有意义,所以-0被定义为-2147483648(32bit系统),这时最高位既是符号位也是真值,所以转换成上述值)

可以看到,这里也被进行循环,这些循环在一些特殊地方有特殊用法,在下还不了解,就不叙述了。

#include <stdio.h>int main(int argc, const char *argv[])
{
#if 0int i = 1;int ret = i >> 8;if (ret== 0)printf("小端存储\n");elseprintf("大端存储\n");
#endifint a,b = -2147483648;a = 0x7fffffff;printf("%d\n",a);a++;                                                                        printf("%d\n",a);printf("%#x\n",a);return 0;
}

gcc编译器测试结果如下:

不对处,欢迎指正!

嵌入式系统学习整理-FourDay-原码反码补码详解相关推荐

  1. 原码, 反码, 补码 详解(转载)

    原码, 反码, 补码 详解(转载) 繁星*墨菲 于 2020-05-23 10:22:53 发布1015 收藏 75 版权 转载地址:原码, 反码, 补码 详解 - ziqiu.zhang - 博客园 ...

  2. 关于计算机中 原码, 反码, 补码 详解

    本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进一步的论证了为何可以用反码, 补码的加法计算原码的减法. 论证部分如有不对的地方请各位牛人帮忙指正! 希 ...

  3. 原码 反码 补码 详解

    一. 机器数和真值 在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念. 1.机器数 一个数在计算机中的二进制表示形式,  叫做这个数的机器数.机器数是带符号的,在计算机用一个数的最高位存放 ...

  4. 原码 反码 补码 详解

    本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进一步的论证了为何可以用反码, 补码的加法计算原码的减法. 论证部分如有不对的地方请各位牛人帮忙指正! 希 ...

  5. 原码, 反码, 补码详解——北大陈向群老师课堂笔记

    一. 机器数和真值 在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念. 1.机器数 一个数在计算机中的二进制表示形式, 叫做这个数的机器数.机器数是带符号的,在计算机用一个数的最高位存放符 ...

  6. 原码, 反码, 补码 详解

    本文转载于: 作者:张子秋 出处:http://www.cnblogs.com/zhangziqiu/ 本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进 ...

  7. 二进制原码反码补码详解

    二进制原码反码补码 首先我们在了解什么是原码,反码,补码之前,我们先来谈谈为什么需要有这些,只要原码不行吗? 答案肯定是不行的!

  8. 源码 反码 补码详解(为什么计算机存储数值为补码形式?)

    一. 机器数和真值 在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念. 1.机器数 一个数在计算机中的二进制表示形式,  叫做这个数的机器数.机器数是带符号的,在计算机用一个数的最高位存放 ...

  9. Java修炼之凡界篇 筑基期 第02卷 语法 番外1 原码 反码 补码

    原码 反码 补码 博客整理自 原码, 反码, 补码 详解 - ziqiu.zhang - 博客园 (cnblogs.com) 机器数与真值 在学习原码.反码和补码之前,需要先了解什么是机器数,什么是真 ...

最新文章

  1. RabbitMQ学习系列二:.net 环境下 C#代码使用 RabbitMQ 消息队列
  2. 西门子200恒压供水梯形图_求西门子plc200恒压供水编程实例(梯形图)急急急......望高手指点!...
  3. clickhouse 航空数据_ClickHouse空间分析运用
  4. 浅谈对xmpp的理解及应用
  5. java自制缓冲池_java String 缓冲池概念的举例说明
  6. cesium模型不遮挡点线面_cesium点线面测试数据
  7. 【译】CodeIgniter HMVC模块扩展使用文档
  8. [转]被当做狗和鸡来驱赶的百姓
  9. m1 Mac的brew包完全卸载方法
  10. 16.通过实现“函数一个返回值出口,返回两个返回值”。来了解指针与普通地址的区别...
  11. 函数调用你知道几种方法
  12. 微信小程序实现下拉刷新
  13. 机器翻译的前世今生(1)
  14. 中国各省份边界json文件
  15. [原创开源项目]EPUBBuilder一款在线的epub电子书编辑工具
  16. Vue移动端rotate强制横屏
  17. PX4自主设置飞行模式
  18. 三星 android 调试模式设置,三星 W2016 开启USB调试模式
  19. sql docker容器_如何将Microsoft SQL Server Docker容器与Azure Data Studio连接
  20. 公司小程序,公众号申请支付流程

热门文章

  1. 英特纽购途索的商业模式是否存在裂变效应?
  2. 浅析选择广告联盟的四大标准
  3. Linux netlink详解
  4. 360使用什么php框架,[项目实战] 马震宇出品360问答系统项目实战 基于HDPHP框架
  5. Discuz!7.2 SQL注入复现实验
  6. Repeater控件中添加删除修改按钮
  7. 微信小程序,ios上日期转时间戳出现的问题
  8. 从零开始java文件服务器篇:文件服务器
  9. jQuery实现图片跟着鼠标移动的效果
  10. 6.0 后台登录》首页 去掉其它的信息