一、机器数和真值、有符号数和无符号数
1、机器数
一个数在计算机中的二进制表示形式,叫做这个数的机器数,机器数有两个特点:
一是符号数字化:我们实用的数由正数和负数之分,由于计算机内部硬件只能表示两种物理状态(0和1),因此数的正号“+”或负号“-”在计算机里就用一位二进制的0或1来区别。通常这个符号放在二进制数的最高位,称符号位,以0代表符号“+”,以1代表符号“-”。例:十进制的+5,计算机字节长为8位,转换为二进制就是 0000 0101,十进制-5转换为二进制就是1000 0101,这里的 0000 0101和1000 0101就是机器数
二是其数的大小受机器字长的限制:机器内部设备一次能表示的二进制位数叫机器的字长,一台机器的字长是固定的。字长8位叫一个字节(Byte),机器字长一般都是字节的整数倍,如字长8位、16位、32位、64位。
2、真值
因为符号位占据第一位,数的形式值就不等于真正的数值,例如上面的有符号数 1000 0101,其最高位1代表负,其真正数值是 -5 而不是形式值133(1000 0101转换成十进制等于133)。所以,为区别起见,带符号位的机器数对应的数值称为机器数的真值。
例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
3、有符号数和无符号数
在计算机中,可以区分正负的类型,称为有符号类型,无正负的类型,称为无符号类型。(你自已决定是否需要有正负。 就像我们必须决定某个值使用整数还是实数,使用多大的范围数一样,自已决定某个值是否需要正负。如果这个变量有负值,那么我们可以定它为有符号类型,反之定义它为无符号类型,Java语言中并没有无符号类型(Unsigned)的基本数据类型,byte、short、int、long都是带符号整数类型,float、double是带符号实数类型,最高位是符号位。PS:char类型属于无符号类型,字符型,2个字节表示16位,取值范围0~65,536,使用Unicode字符集编码表示)
如果是有符号类型数值,使用二制数中的最高位表示正负。 比如1个字节的数据类型,有8位,最高位是符号位,0代表正数,1代表负数,后面7位是数值位。无符号数中,所有的位都用于直接表示该值的大小。
无符号数:1111 1111 值:255=1*27+1*26+…=2^n-1
有符号数:0111 1111 值:127
同样一个字节,无符号的最大值是255,有符号的最大值是127
二、原码、反码、补码基础概念和计算方法
对于一个数, 计算机要使用一定的编码方式进行存储.原码,、反码、补码是机器存储一个具体数字的3种具体编码表示方式().
1、原码
最高位为符号位,0代表正数,1代表负数,非符号位为该数字绝对值的二进制表示。
十进制 | 原码 |
---|---|
+1 | 0000 0001 |
-1 | 1000 0001 |
第一位为符号位,0代表正数,1代表负数,所以8位二进制数的取值范围为 |
[1111 1111, 01111 1111] 即 [-127, 127]
1111 1111 | 1111 1110 | … | 1000 0001 | 1000 0000 | 0000 0000 | 0000 0001 | … | 0111 1110 | 0111 1111 |
---|---|---|---|---|---|---|---|---|---|
-127 | -126 | … | -1 | -0 | +0 | 1 | … | 126 | 127 |
2、反码 | |||||||||
反码表示法: 正数的反码就是其本身,负数的反码是原码除符号位外按位取反 | |||||||||
十进制 | 原码 | 反码 | |||||||
– | – | – | |||||||
+1 | 0000 0001 | 0000 0001 | |||||||
-1 | 1000 0001 | 1111 1110 |
取值范围为
1000 0000 | 1000 0001 | … | 1111 1110 | 1111 1111 | 0000 0000 | 0000 0001 | … | 0111 1110 | 0111 1111 |
---|---|---|---|---|---|---|---|---|---|
-127 | -126 | … | -1 | -0 | +0 | 1 | … | 126 | 127 |
3、补码 | |||||||||
补码表示法:正数的补码就是其本身,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1) | |||||||||
十进制 | 原码 | 反码 | 补码 | ||||||
– | – | – | – | ||||||
+1 | 0000 0001 | 0000 0001 | 0000 0001 | ||||||
-1 | 1000 0001 | 1111 1110 | 1111 1111 | ||||||
取值范围为 | |||||||||
1000 0000 | 1000 0001 | 1000 0010 | … | 1111 1111 | 0000 0000 | 0000 0001 | … | 0111 1110 | 0111 1111 |
– | – | – | – | – | – | – | – | – | – |
-128 | -127 | -126 | … | -1 | 0 | 1 | … | 126 | 127 |
三、为什么要使用原码、反码、补码
上面我们介绍了计算机可以使用3种编码方式表示一个数,对于正数而言,原码、反码和补码的表示结果都相同,但是对于负数的表示是完全不同
[-1] = [10000001]原 = [11111110]反 = [11111111]补
可以看见原码表示法是最容易理解的,最高位0或1表示正负,数值位表示具体的大小,但是为什么还会有反码和补码出现呢?
存在即合理,首先无论我们使用哪一种表示方式,最终存在计算机中都是一系列0和1组成的二进制,既然是数值,肯定要参与基础运算(加减乘除),由于我们定义为第一位为符号位,0代表正数,1代表负数,如果让计算机辨别符号位显然会让计算机的基础电路设计变得更加复杂,依据数学运算法则定律:减一个正数等于加上一个负数,即1-1=1+(-1) =0。这样就可以用加法表示减法,而乘法是加法的累积,除法是减法的累积,所以在计算机中只要有加法就够了。不需要让计算机辨别符号位的话那么运算的时候能否直接让符号位参与运算呢
原码:计算十进制表达式:5-3=2
5 - 3 = 5 + (-3) = [0000 0101]原 + [1000 0011]原 = [1000 1000]原 = -8
显然用原码表示的话直接让符号位参与运算结果是不正确的
反码:计算十进制表达式:5-3=2
5 - 3 = 5 + (-3) = [0000 0101]原 + [1000 0011]原 = [0000 0101]反 + [1111 1100]反 = [
10000 0001]反(因为进位溢出截取最高位1) = [0000 0001]反 = [0000 0001]原 = 1
反码的运算结果也不正确,而且对于原码和反码而言还存在+0和-0两个特殊数值, 显然0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.
补码:计算十进制表达式:5-3=2
5 - 3 = 5 + (-3) = [0000 0101]原 + [1000 0011]原 = [0000 0101]反 + [1111 1100]反 = [0000 0101]补 + [1111 1101]补 = [
10000 0010]补(因为进位溢出截取最高位1) = [0000 0010]补 = [0000 0010]原 = 2
补码的运算结果正确,并且补码表示法也解决了原码和反码中存在+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]补
注意这里实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的
使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].
四、原码, 反码, 补码深入
补码是根据同余的概念引入的,我不擅长深入讲解这方面理论的东西,推荐大家可以看看我看见的几篇有趣的文章
为什么计算机采用补码而不是原码或反码?
计算机补码运算背后的数学原理是什么?