从计算机中数据类型的存储方式,思考理解原码,反码,补码
从计算机中数据类型的存储方式,思考理解原码,反码,补码
1. 数据类型
首先,我们知道,在C中,设计了两个类型的数据:
- 有符号数据类型
- 无符号数据类型
==== signed & unsigned
打印方式: signed: %d, unsigned: %u
数据范围:
signed: [-128, 127]
unsigned: [0, 255]
2. 无符号数据类型 - 原码,反码,补码
在学习计算机的过程中,很快出现了:原码,反码,补码的概念。这里你肯定不会理解为什么要设计这个玩意儿的! 因为你不知道 “有符号数据类型” 在 计算机中是怎么存储的。你需要看下一段。
1. 反码,补码是针对有符号类型的数来讲的,对于unsigned 没有意义;
2. 补码 一般用来表示负数;
对于一个带符号的数字来说,比如:signed int i = 7和 signed int j = -7
正数:(7)(正数首位为0)
原码:0000 0111
补码:0000 0111 (正数的补码等于原码,这个是规定,不要考虑正数是否有反码)
负数:(-7)(负数首位为1)
原码:1000 0111
反码:1111 1000 反码=(对于负数来说,首位不变,其他位反转)
补码:1111 1001
3. 数据的存储:
在这里,你需要理解一下,计算机对有符号数据类型,和无符号数据类型的存储方式不同;
无符号数的存储:
无符号数没有原码,补码之说,直接转换成二进制;
有符号数的存储:
- 有符号数采用补码的形式存储,无论你是正数还是负数,只要你是有符号数;(重要)
- 正数的补码,等于其原码
- 负数的补码,等于反码加1
4. 特殊的0的编码问题
在有符号的数据类型下,0会出现两个表达方式:+0, -0
那么首先,我们看看如果按照其他负数的逻辑,+0 和 -0的反码分别怎么表示?
原则: 对于负数,首位不参与运算,负数的补码等于反码加一;那么,-0的补码等于:1000 0000 +0的补码等于:0000 0000
+0 和 -0
+0:
原码:0000 0000
补码:0000 0000
-0:
原码:1000 0000
反码:1111 1111
补码:1000 0000
什么?0 虽然在signed情况下,有两种表达方式(+0,和-0),但是毕竟都是一个数0啊,怎么0可以有两个补码?不允许啊,这台浪费了吧!!!
因此,在不能浪费的理念下,计算机又新增规定:对于0有+0, -0这个特殊情况下,这个时候,最高符号位要参与运算;此时,无论是数字0来说,无论是+0 还是 -0,其补码只有一个,那就是0000 0000;
这个时候,你又会发现,多余了一个1000 0000 没人用吗?怎么办呢? 在计算器中,又出台规定将这个没有用的1000 0000表示-128;
所以,你会看到,对于有符号数,数据存储范围多了一个:-128
数据范围:
- 有符号数:[-128, 127]
- 无符号数:[0, 255]
5. 使用补码的好处?
看到了特殊的0的解释之后,你会发现,在有符号数据类型中,使用补码的好处是:
- 解决了0的编码问题
- 可以多保存一个数:-128, 如signed char 可以多保存一个:-128
- 多保存的一个数:-128,没有原码和反码
6. 为什么使用补码来存储数据?
- 解决0的编码问题
- 减法运算可以转化为加法运算,省去硬件上的减法电路,CPU只需要有:全加器,求补电路
7. 思考 - 为什么设计补码?
规则: 补码 = 反码 + 1
我们已经知道对于“符号类型的数”,他的补码等于反码加一,我们思考一下,这个规则是怎么来的?想明白了,也就明白为什么要设计“补码”了!
首先,我们知道 7的二进制原码是:0000 0111
那么我们思考一下,什么值 和 7 相加等于0?
小学生都知道,答案是:-7,于是,你会发现好几个二级制数字,都能满足这个条件,难道让他们都表示-7吗? 肯定不可能啊!
1111 1001
1111 1010
1111 1100
现在核心思想就在这里,计算机用巧妙之处体现出来了! 计算机想了,既然他们都满足条件,我是不可能让他们都表示为-7的,我要重新设计一个规则,使得,只有一个数满足!于是这个规则就出现了:
先设计一个数,可以和7相加后等于: 1111 1111 , 然后再加1 就等于0了;
所以:
7:
0000 0111 = 1111 1111
+
1111 1000
所以,-7在存储的时候一定要存成:1111 1001 ,而不是:-7的原码(1000 0111), 那么我们就设计一个概念“补码”, 使得-7的补码等于要存储的值(1111 1001), 从-7的原码到-7的补码就是我们的规则:首位不变,其他位反转成“反码”,然后再加1;