-
整理了下关于原码、反码、补码的相关内容,以及负数的表示和加法运算。
-
原码、反码和补码:
- 原码:机器码,原本的表示法
- 反码:除符号位(最高位)外取反
- 补码:反码加1,正数的补码反码都是原码本身
- 手动求补码:先写出该负数的相反数(正数),再将该正数的二进制写出来,对二进制取反,最后加1。
-
二进制:计算机底层的存储都是以二进制形式存储,最小单位为bit,存放一位二进制,0或1,计算机处理数据最基本单位是字节(Byte,简称B),8个二进制位为1个字节,即:1B = 8bit。
-
java 基本数据类型对应的存储空间:
数据类型 存储空间 bit数 取值范围 boolean 1字节 1 * 8 true(0000 0001),false(0000 0000) byte 1字节 1 * 8 char 2字节 2 * 8 short 2字节 2 * 8 int 4字节 4 * 8 long 8字节 8 * 8 float 4字节 4 * 8 double 8字节 8 * 8 -
数据类型所允许的最大值:与二进制位数有关,一个字节所允许的最小值为:0000 0000,最大值为:1111 1111,即
所以一个n位的二进制允许的最大值的计算方式:
-
-
有符号数和无符号数:在计算机中,需要区分正负的数据类型,称为有符号数据类型,例如int,包含正数也包含负数;不需要区分正负的数据类型,称为无符号数据类型。
- 无符号类型:以单字节数为例,如果指定一个数据类型为无符号类型,那么最高位也就是最左边的一位,代表的意义和其他位相同,表示数的大小:无符号二进制1000 0000 --> 十进制 128。它的取值范围:0 ~ 255
- 有符号类型:以单字节数为例,指定一个数据类型为有符号类型,那么最高位称为符号位,不再代表数字大小,作为正负数的标志,如果符号位是1,代表负数,如果是0,代表正数。单字节数的取值范围:-128 ~ 127,表示的数字个数不变,范围改变。
- 负数:由于在负数中,-1是最大的负整数,所以在用二进制表示时,无论是几个字节数,都使用全1来表示 -1: 1111 1111。-1-1=-2: 1111 1110,依次减下去可以获取单字节的最小负值,-128: 1000 0000。
- 使用反码和补码的意义:原码是由十进制转为二进制所得,反码是由原码除符号位依次取反所得,补码是在反码基础上+1。
-
对于正数三种编码相同:
-
对于负数则不同:
首先,负数的原码是由正数1加负号,也就是符号位改为1得到,之所以这样设计是为了让符号位也参与计算,使得减法也能用加法表示,毕竟减去一个数等于加上一个负数,
简化基本运算器的设计。- 原码的计算方式:
很明显以原码来计算是不正确的。为了解决这种问题,引入了反码。 - 反码的计算方式:
由结果可见,计算结果数值是对的,但是这就导致了另外一个问题,0这个数字带符号是没有意义的,现在就变成了一个 +0编码和一个 -0编码,针对这两个编码表示0的问题,引入了补码。 - 补码的计算方式:
这样计算结果是正确的,也不会出现正负0的情况,那么对于 1000 0000可以用来表示 -128,补码的出现既解决了正负0问题,也使得表示的负数多了一位。所以一个字节有符号数用补码表示的取值范围为 -128 ~ 127,用反码或原码表示的取值范围为 -127 ~ 127。(由于 1000 0000是用-0的补码来表示-128,所以-128并没有原码和反码表示) - 在计算机内部都是使用补码来计算和存储。补码所依据的数学理论是同余,具体论证过程。
- 原码的计算方式:
-
- 使用反码和补码的意义:原码是由十进制转为二进制所得,反码是由原码除符号位依次取反所得,补码是在反码基础上+1。
-
一个具体的Java例子:
- 问题如下:
int 类型数值128强转为byte类型,打印结果,输出为什么是-128?
- 解释:
- int类型128的原码:0000 0000 0000 0000 0000 0000 1000 0000,反码和补码相同
- 当int被强转为byte类型时,由于byte只有1个字节,截断高24位,保留低8位:1000 0000
- 在进行打印时,short、byte类型都会作为int类型计算,此时会进行有符号填充操作,前24位都填1,1111 1111 1111 1111 1111 1111 1000 0000
- 对补码 -1 再取反结果为:1111 1111 1111 1111 1111 1111 0111 1111
- 最终结果:1000 0000 0000 0000 0000 0000 1000 0000,由于除符号位以外,高位都是0,有效位为8位,128,加上标志位,结果是-128
- 这个小问题涉及到了最基础的编码知识,还是值得探究一番。
- 问题如下: