1.计算机底层存储数据使用的是二进制。
2.计算机存储最小单位是bit,即1个二进制位。
3.一个数在计算机的存储形式是二进制数,我们称这些二进制数为机器数,机器数是有符号,在计算机中用机器数的最高位存放符号位,0表示正数,1表示负数。
4.因为带有符号位,所以机器数的形式值不等于其真值,以机器数1000 0111为例,其真正表示的值为-7(最高位1代表负数,不参与运算),而形式值为135。将带符号的机器数的真正表示的值称为机器数的真值。
// 二进制数
1000 0111
// 形式值 的10进制
0*2^7 0*2^6 0*2^5 0*2^4 0*2^3 1* 2^2 1*2^1 1*2^0 = 135
// 真值,1(最高位1表示负数,转10进制时不参数运行)000 0111
- (0*2^6 0*2^5 0*2^4 0*2^3 1* 2^2 1*2^1 1*2^0) = -7
5.计算机在存储一个数字时并不是直接存储该数字对应的二进制数字,而是存储该数字对应二进制数字的补码。
6.原码、反码、补码。
- 原码
原码的表示与机器数真值表示的一样,即用第一位表示符号,其余位表示数值。
十进制数 |
原码 |
1 |
0000 0001 |
-1 |
1000 0001 |
- 反码
- 正数的反码是其原码本身。
- 负数的反码是在其原码的基础上,符号位不变,其余各位取反。
十进制数 |
原码 |
原码 |
1 |
0000 0001 |
0000 0001 |
-1 |
1000 0001 |
1111 1110 |
- 补码
- 正数的补码是其原码本身。
- 负数的补码是在其原码的基础上,符号位不变,其余各位取反后加1(即在反码的基础上加1)。
十进制数 |
原码 |
原码 |
补码 |
1 |
0000 0001 |
0000 0001 |
0000 0001 |
-1 |
1000 0001 |
1111 1110 |
1111 1111 |
7.数据的存储大体流程:
计算机实际只存储补码, 所以原码转换为补码的过程,也可以理解为数据存储到计算机内存中的过程:
在原、反、补码中,正数的表示是一模一样的,而负数的表示是不相同的,所以对于负数的补码来说,我们是不能直接用进制转换将其转换为十进制数值的,因为这样是得不到计算机真正存储的十进制数的,所以应该将其转换为原码后,再将转换得到的原码进行进制转换为十进制数。
为何使用补码直接使用 “机器数(即原码)” 表示再进行计算不好吗?为啥还要有反码,补码?
因为计算机底层只能进行加法运算。所以在存储数时进行算法处理,运算时直接取出来进行加法运算,运算完结果直接存储,思路清楚简单,并且不让计算机关心符号位,让符号位参与到运算中去。用小学学的知识把减法转成加法就是这样: 1 - 1 = 1 (-1) = 0。
下面通过1-1 = 0 的例子分别看一下存储原码、反码、补码哪个才能得到正确的答案:
1 - 1 = 1 (-1)
1.存储时为原码:
(1的原码)0000 0001 (-1的原码)1000 0001 = (结果原码)1000 0010
1000 0010 转为 机器数的真值为 -2,明显 1-1 = -2是错误的,所以不能使用原码存储。
2.存储为反码:
(1的原码)0000 0001 (-1的原码)1000 0001
(1的反码)0000 0001 (-1的反码)1111 1110 = (结果反码)1111 1111
1111 1111 为反码,需要转为原码 1000 0000,再转10进制才是 机器数的真值 -0。结果看似是正确的,但如果有 0 和 -0 看起来怪怪的,因为0的正负是相同的。而使用[0000 0000]和[1000 0000] 都表示 0 有些浪费,所以才有了补码。
3.存储为补码:
(1的原码)0000 0001 (-1的原码)1000 0001
(1的反码)0000 0001 (-1的反码)1111 1110
(1的补码)0000 0001 (-1的补码)1111 1111 = (结果补码)0000 0000
0000 0000 为补码,需化为原码也为 0000 0000,而以前出现问题的-0则不存在了,那么也不能浪费呀,可以使用-0的补码(1000 0000)来表示 -128 ,正好推算也成立。推算过程如下:
(-1) (-127) = -128
(-1的原码)1000 0001 (-127的原码)1111 1111
(-1的补码)1111 1111 (-127的补码)1000 0001 = (结果补码)1000 0000
所以补码 1000 0000 表示 -128,比较特殊,是人们强制规定的,所以-128并没有原码和反码表示,只要补码是[1000 0000],其十进制数值就为-128。
因为补码多存储一个-128,而且在计算机底层中存储的是补码,所以在计算机中一个8位的二进制数的存储范围是用补码表示的[-128,127],而不是用原码或反码表示的[-127,127]。这也可以解释为什么计算机中一个字节的取值范围是[-128,127]。
整数的范围本例子以32位系统为例,64位同理。1个int在32位系统上是 4个Byte(字节),4 * 8 = 32bit,即32个二进制位。
- int 正数的最大表示为:
原码:0111 1111 1111 1111 1111 1111 1111 1111
反码:0111 1111 1111 1111 1111 1111 1111 1111
补码:0111 1111 1111 1111 1111 1111 1111 1111
根据原码转十进制为:2147483647
- int 负数的最大表示为:
原码:1111 1111 1111 1111 1111 1111 1111 1111
反码:1000 0000 0000 0000 0000 0000 0000 0000
补码:1000 0000 0000 0000 0000 0000 0000 0001
根据原码转十进制为:-2147483647
同上面补码的 -0 此处的 -0 补码:1000 0000 0000 0000 0000 0000 0000 0000 可以当做最小的负数 -2147483647 ( -1) = -2147483648
总结:- 掌握原码、反码、补码的意义。
- 32位的系统 有符号的 int (整数)的范围为:2147483647 ~ -2147483648
既然学习编程就一定把这些和语言无关的基本知识点弄懂。除了整数存储,浮点数存储也是一个值得研究的知识点。比如你在浏览器的 `开发者工具` -> `Console` 里输入 0.1 0.7 看看结果是多少?为什么会这样呢?后面再好好研究一下。
,