嵌入式系统学习整理-FourDay-原码反码补码详解
目录
一、前言
一、原码、反码、补码定义
1. 原码
2. 反码
3. 补码
二、补码的加减计算
一、前言
首先,要知道计算机底层都以二进制表示,数字也不例外,那么,如果数字加减运算的话,要怎么算呢?
在计算机内部只有加法器,既产生数的和的装置,在电子学中,加法器为一种数位电路,可进行加法运算。
对于十以内的加法运算,有相关的4个量:
- 被加数 A
- 加数 B
- 数字相加之和 S
- 相加之后的进位 C
为什么考虑进位呢?要想想计算机不是人,他要按部就班的进行计算,我们初学加法不也是进行进位运算嘛,所以这一位记录是整个运算算法必要的数据。
十进制我们了解了,那么二进制有什么不一样呢?
二进制只有0和1,1+1就会产生进位,如果我们只考虑1+1的话,用上面四个数据就可以了,但是,只有1+1能做什么呢?变成两位怎么算?那每一位要怎么做?
比如11 + 01(从右向左一次为第一位和第二位)第一位相加要进位,那么对于第二位来说是不是要处理第一位产生的进位呢!这时假设第二位是你要操作的位,就要比第一位多出一个要处理的数据。
所以,对于二进制普遍处理而言,二进制加法要处理5个量:
- 被加数 A
- 加数 B
- 前一位的进位 CIN
- 数字相加之和 S
- 相加之后的进位 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-原码反码补码详解相关推荐
- 原码, 反码, 补码 详解(转载)
原码, 反码, 补码 详解(转载) 繁星*墨菲 于 2020-05-23 10:22:53 发布1015 收藏 75 版权 转载地址:原码, 反码, 补码 详解 - ziqiu.zhang - 博客园 ...
- 关于计算机中 原码, 反码, 补码 详解
本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进一步的论证了为何可以用反码, 补码的加法计算原码的减法. 论证部分如有不对的地方请各位牛人帮忙指正! 希 ...
- 原码 反码 补码 详解
一. 机器数和真值 在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念. 1.机器数 一个数在计算机中的二进制表示形式, 叫做这个数的机器数.机器数是带符号的,在计算机用一个数的最高位存放 ...
- 原码 反码 补码 详解
本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进一步的论证了为何可以用反码, 补码的加法计算原码的减法. 论证部分如有不对的地方请各位牛人帮忙指正! 希 ...
- 原码, 反码, 补码详解——北大陈向群老师课堂笔记
一. 机器数和真值 在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念. 1.机器数 一个数在计算机中的二进制表示形式, 叫做这个数的机器数.机器数是带符号的,在计算机用一个数的最高位存放符 ...
- 原码, 反码, 补码 详解
本文转载于: 作者:张子秋 出处:http://www.cnblogs.com/zhangziqiu/ 本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进 ...
- 二进制原码反码补码详解
二进制原码反码补码 首先我们在了解什么是原码,反码,补码之前,我们先来谈谈为什么需要有这些,只要原码不行吗? 答案肯定是不行的!
- 源码 反码 补码详解(为什么计算机存储数值为补码形式?)
一. 机器数和真值 在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念. 1.机器数 一个数在计算机中的二进制表示形式, 叫做这个数的机器数.机器数是带符号的,在计算机用一个数的最高位存放 ...
- Java修炼之凡界篇 筑基期 第02卷 语法 番外1 原码 反码 补码
原码 反码 补码 博客整理自 原码, 反码, 补码 详解 - ziqiu.zhang - 博客园 (cnblogs.com) 机器数与真值 在学习原码.反码和补码之前,需要先了解什么是机器数,什么是真 ...
最新文章
- RabbitMQ学习系列二:.net 环境下 C#代码使用 RabbitMQ 消息队列
- 西门子200恒压供水梯形图_求西门子plc200恒压供水编程实例(梯形图)急急急......望高手指点!...
- clickhouse 航空数据_ClickHouse空间分析运用
- 浅谈对xmpp的理解及应用
- java自制缓冲池_java String 缓冲池概念的举例说明
- cesium模型不遮挡点线面_cesium点线面测试数据
- 【译】CodeIgniter HMVC模块扩展使用文档
- [转]被当做狗和鸡来驱赶的百姓
- m1 Mac的brew包完全卸载方法
- 16.通过实现“函数一个返回值出口,返回两个返回值”。来了解指针与普通地址的区别...
- 函数调用你知道几种方法
- 微信小程序实现下拉刷新
- 机器翻译的前世今生(1)
- 中国各省份边界json文件
- [原创开源项目]EPUBBuilder一款在线的epub电子书编辑工具
- Vue移动端rotate强制横屏
- PX4自主设置飞行模式
- 三星 android 调试模式设置,三星 W2016 开启USB调试模式
- sql docker容器_如何将Microsoft SQL Server Docker容器与Azure Data Studio连接
- 公司小程序,公众号申请支付流程