引言
本篇文章属于计算机基础通识,主要讨论:有符号类型、无符号类型的区别,byte、int 等类型的取值范围,最大值最小值的计算公式的由来,原码、反码、补码转换公式。
有符号类型与无符号类型
在 Java 中的八大基本类型:byte、short、int、long、float、double、boolean、char 中,前六个为有符号类型。
有符号类型 | 无符号类型 |
---|---|
byte | boolean |
short | char |
int | |
long | |
float | |
double |
所谓有符号类型,指的是这种类型本身可以表示负数和正数。注意与整数相互区分。
有符号类型都遵从相同的数值表示规则,即最高位表示符号位。下面以 byte 为例进行讲解。
byte 类型的取值范围
我们知道,byte 类型只表示一个字节,那么它究竟是如何表示正数和负数的呢?
对于有符号类型,我们会将 byte 的 8 位分成两部分——符号位和数值位:
那么byte 的取值范围就是 -2^7 ~ (2^7 - 1),即 -128 ~ 127。
为什么?
首先我们知道:进制、位数和取值范围是乘方的关系。底数代表进制,指数代表位数,所得结果就表示取值数量(地址空间)。如下图所示:
对于正数,取值范围表示的是能够表示的数字的个数,是包含 0 的,就好像数组中长度与最后一个数的索引值,最大值也需要进行减 1 处理,因此最大值就是 2^7 - 1,即 127。
对于负数,符号位用 1 表示,然而如此一来就会出现 -0 和 0 这两个奇怪的数,于是人为规定 1 000 0000 表示 byte 类型范围内的最小值。此时,负数表示的情况下,7 位数值的全部取值都表示一个对应的负数,而正数要去掉 1 个 全 0 的值用来表示数字 0,因此很明显负数要比正数多 1 个数。即 负数可以有 2^7 个,于是最小的数就是 -2^7 ,即 -128。
原码、反码、补码
原码,反码,补码的产生过程,就是为了解决,计算机做减法和引入符号位(正号和负号)的问题。
原码(true form)是一种计算机中对数字的二进制定点表示方法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为0,负数该位为1(0有两种表示:+0和-0),其余位表示数值的大小。一般情况下,原码就单独处理符号位的一种表示方式,如 -5的8位原码是:1000 0101;-3 的 8位原码是 1000 0011;8 的原码是 0000 1000。注意,如果某个负数取绝对值后超出位数所能表示的最大值,比如 -128 ,取模后是128 超出了 byte 类型的表示最大值 127,那么 -128 就没有原码。
反码是原码除符号位之外的按位取反。如 -5 的 8 为反码是 1111 1010;-3 的反码是 1111 1100 ;8的反码是 0111 0111。
在计算机系统中,数值一律用补码来表示和存储。
原因在于,原码虽然简单直观,但是计算的时候需要特别将符号位和数值位区分处理,这无形增加了硬件的开销和复杂性,而使用补码,可以将符号位和数值位统一处理。
上面部分提到了有符号数的取值范围,它们包含了许多负数,然而,如果单从表面上观察,很难将一个负数与其计算机内部的二进制表现形式联系起来,比如:byte 类型的 -128,它在计算机内部的表示是:1000 0000;-125 对应 1000 0011 。
那么我们就需要了解原码、反码、补码的转换规则才能快速完成 编码的转化。
首先,对于正数来说,人为规定:原码、反码、补码完全一样。除了高位为 0 以外,剩余的 数值位,可以快速通过 2进制与 16进制的转化规则确定,比如 byte 类型的 125 ,十六进制是:7D,那么原码、反码、补码都是 0111 1101 。
对于负数,补码(计算机中实际表示的编码)的计算规则为:原码取反再加一。
另外,补码到原码的转换也可以通过 先取反,再加一的方式得到,不需要先减一再取反,这样的特性降低了电路逻辑的复杂性,也是补码流行起来的原因。
short、int 等有符号类型的类推
short 、 int 等其他有符号类型的值,除了字节数不同、位数较多以外,其他所有的规律都是类似的。
如int 类型,它在计算机中以 4 个字节表示,总共32 位,最高位也同样表示 正负,那么数值位总共就有 31 位,那么 int 的表示范围就是 : -2^31 ~ (2^31 - 1)。推理方式和 前面 byte 是完全一样的。
总之,一定要记住,在计算机中,所有负数 都是由补码来表示的,而正数 直接用原码表示(但实际上也是补码,只不过规定正数的补码、反码和 原码要完全一致)。