计算机对外信息展示方式有文字、图片、音频、视频等,但不论应用在什么地方,信息在机器内部的形式都是一致的,即均为0和1组成的各种编码。
在计算机中参与运算的数有两大类:无符号数和有符号数。
计算机中的数均放在寄存器中,通常称寄存器的位数为机器字长。
所谓无符号数,即没有符号的数,在寄存器中的每一位均可用来存放数值。当存放有符号数时,则需留出位置存放符号。
因此,在机器字长相同时,无符号数与有符号数所对应的数值范围是不同的。
以机器字长为16位为例,无符号数的表示范围为 0~65535,而有符号数的表示范围为 -32768 ~ +32767(此数值对应补码表示)。
对有符号数而言,符号的“正”、“负”机器是无法识别的,但由于“正”、“负”恰好是两种截然不同的状态,如果用“0“表示“正”,用“1”表示“负”,这样符号也被数字化了,并且规定将它放在有效数字的前面,即组成了有符号数。
例如,+1100 在机器中表示为 01100 ,-1100 在机器中表示为 1 1100 。
在Java语言中,如 int、float 等都是有符号数,故下文所讲都是在有符号数的范畴。
把符号(如+、-)“数字化”的数称为机器数,而把带“+”或“-”符号的数称为真值。
一旦符号数字化后,符号和数值就形成了一种新的编码。在运算过程中,符号位能否和数值部分一起参加运算?如果参加运算,符号位又需作哪些处理?这些问题都与符号位和数值位所构成的编码有关,这些编码就是原码、补码、反码和移码。
原码是机器数中最简单的一种表示形式,符号位为0的表示正数,符号位为1的表示负数,数值位即真值的绝对值,故原码表示又称为带符号的绝对值表示。
正数的反码依旧等于原码,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。
正数的补码码依旧等于原码,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1。 (即在反码的基础上+1)
同一个真值的移码和补码仅差一个符号位,若将补码的符号位由“0”改为“1”,或从“1”改为“0”,即可得该真值的移码。简单说,无论正负数,只要将其补码的符号位取反即可。
小数点固定在某一位置的数为定点数,有以下两种格式。
当小数点位于数符和第一数值位之间时,机器内的数为纯小数;当小数点位于数值位之后时,机器内的数为纯整数。
采用定点数的机器称为定点机。数值部分的位数n决定了定点机中数的表示范围。若机器数采用原码,小数定点机中数的表示范围是 -(1 - 2^-n) ~ (1 - 2^-n),整数定点机中数的表示范围是 -(2^n-1) ~ (2^n-1)。
实际上计算机中处理的数不一定是纯小数或纯整数(如圆周率3.1416),而且有些数据的数值范围相差很大(如电子的质量9×10-6g,太阳的质量2×103g),它们都不能直接用定点小数或定点整数表示,但均可用浮点数表示。浮点数即小数点的位置可以浮动的数,如
352.47 = 3.5247 * 10^2 = 3524.7 * 10^-1
显然,这里小数点的位置是变化的,但因为分别乘上了不同的 10 的方幂,故值不变。
通常,浮点数被表示成 N = S * r^j , 式中,S为尾数(可正可负),j为阶码(可正可负),r是基数(或基值)。在计算机中,基数可取2,4、8或16等。
浮点数的规格化形式为 N = 0.110101 * 2^10。
浮点数在机器中的形式如下所示。
浮点数由阶码j和尾数S两部分组成。阶符表示阶码数值部分的正负,数符表示尾数数值部分的正负。阶码j反映了浮点数的表示范围及小数点的实际位置。尾数是小数,其位数n反映了浮点数的精度。
定点数和浮点数可从如下几个方面进行比较。
①当浮点机和定点机中数的位数相同时,浮点数的表示范围比定点数的大得多。
②当浮点数为规格化数时,其相对精度远比定点数高。
③浮点数运算要分阶码部分和尾数部分,而且运算结果都要求规格化,故浮点运算步骤比定点运算步骤多,运算速度比定点运算的低,运算线路比定点运算的复杂。
④在溢出的判断方法上,浮点数是对规格化数的阶码进行判断,而定点数是对数值本身进行判断。例如小数定点机中的数其绝对值必须小于1,否则“溢出”,此时要求机器停止运算,进行处理。为了防止溢出,上机前必须选择比例因子,这个工作比较麻烦给编程带来不便。而浮点数的表示范围远比定点数大,仅当“上溢”时机器才停止运算,故一般不必考虑比例因子的选择。
总之,浮点数在数的表示范围、数的精度、溢出处理和程序编程方面(不取比例因子)均优于定点数。但在运算规则、运算速度及硬件成本方面又不如定点数。因此,究竟选用定点数还是浮点数,应根据具体应用综合考虑。一般来说,通用的大型计算机大多采用浮点数,或同时采用定、浮点数;小型、微型及某些专用机、控制机则大多采用定点数。当需要作浮点运算时,可通过软件实现,也可外加浮点扩展硬件(如协处理器)来实现。
现代计算机中,浮点数一般采用 IEEE 制定的国际标准,这种标准形式如下:
按 IEEE 标准,常用的浮点数有三种:
符号位S | 阶码 | 尾数 | 总位数 | |
---|---|---|---|---|
短实数 | 1 | 8 | 23 | 32 |
长实数 | 1 | 11 | 52 | 64 |
临时实数 | 1 | 15 | 64 | 80 |
其中,S为数符,表示浮点数的正负,但与其有效位(尾数)是分开的。
阶码的真值都被加上一个常数(偏移量),如短实数、长实数和临时实数的偏移量用十进制数表示分别为 127、1023 和 16383。
尾数部分通常都是规格化表示,即写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。IEEE 754规定,在计算机内部保存尾数时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。
下面列出了十进制数 178.125 的实数表示。
实数表示 | 数值 | ||
---|---|---|---|
原始十进制数 | 178.125 | ||
二进制数 | 10110010.001 | ||
二进制浮点表示 | 1.0110010001*2^111 | ||
符号 | 偏移的阶码 | 有效值 | |
短实数表示 | 0 | 0000 0111+0111 1111 =1000 0110 |
0110 0100 0100 0000 0000 000 |
注:二进制浮点数的基数是2;基数2的指数111是未偏移的阶码;有效值只保留了尾数的小数部分0110010001。
你也可以编写 java 程序来验证在机器上浮点数是否是以 IEEE754 标准存储的,示例代码如下:
@Test
public void test(){
int i = Float.floatToIntBits(178.125f);
System.out.println(Integer.toBinaryString(i));
//0 10000110 01100100010000000000000
}
[1] 浮点数的二进制表示 阮一峰