跟大家分享下Java的浮点数这一问题。
在此类问题中,需要注意的是float型常量需要后缀f或F,例如3.14f,double型可以后缀d,但允许省略。初学者的一个疑惑就是 为什么表达式:
0.4==0.4f
的值是false,而不是true(实际上0.4f>0.4的值是true).
希望这个小文章,能解开初学者的疑惑(没解开也不影响编程).
从数学上看精度
0.4如果表示成二进制是:
0.0110 0110 0110 0110 ... ...
也就是说,是一个无穷级数,即(因编辑器无法表示幂,我用2(-n)表示2的-n次幂)
0.4 =
0*2(-1)+1*2(-2)+1*2(-3)+0*2(-4)... ...
从数学上看,截取无穷级数的任何有限项都是0.4的近似值,这就意味着截取的项越多,精度越高。
2
IEEE 754 浮点数标准
早期的计算机是不能处理浮点数的,直到IEEE 754 标准出现后,计算机才能处理浮点数。仔细讨论IEEE 754 标准肯定超出了本小文章的范围(应该是在《计算机组成原理》这门课程里讲授。会涉及掩码,指数,尾数以及许多知识),
为了通俗理解,解开疑惑,我们首先想到一个基本的事实是,不管它是啥标准,计算机只能用0,1组成的序列表示数据,或者说计算的(硬件)存储系统和计算机计算系统处理的都是0,1组成的序列。
例如,对于:
float x = 0.4f;
x被分配4个字节,占32位,那么32位的各个位上的0,1数字是怎样的呢?
根据IEEE 754 标准就是(32位):
10011001100110011001101
根据IEEE 754 标准,对于float单精度,第 31 位(左边第1位)表示浮点数字的符号。第 30-23位(8位)表示指数(指数加完偏移量,即加偏移量127后的值)。第 22-0 位是尾数(尾数是23位)。如图上图所示意。
我们简单解释一下:
0.4的二进制是
0.0110 0110 0110 0110 0110 0110 0110 0110 ...
根据IEEE 754,首先移动小数点(向左或向右),保证整数部分只有一位非零的1(浮点的来历,对0.4就是小数点向右移动两位):
1.100110011001100110011001100110 ...
那么指数是-2(从数学计算上看,前者是后者乘以2的-2次幂得到的)
那么根据IEEE 754 ,0.4的IEEE 754标准表示是:
符号位(第31位)是 0
指数是-2+127(加偏移量127,理由不必太懂)
尾数是:10011001100110011001101
尾数(第 22-0 位是尾数,共23位)就是从
1.100110011001100110...
小数点后面取23位(事先将第24位加1,可能进位到第23位)。
对于:
double y = 0.4;
y被分配8个字节,占64位,那么64位各个位上的0,1数字是怎样的呢?
根据IEEE 754 标准就是(64位):
00111111110110011001100110011001
10011001100110011001100110011010
根据IEEE 754 标准,对于double双精度,第 63 位表示浮点数字的符号。第 62-52 位(11位)表示指数(指数加完偏移量)。第 51-0 位是尾数(尾数是52位,尾数位比float多。尾数位越多,精度越高)。
当然我们不要按着脑海里通常的想法去把上面的0,1序列转化为熟悉的十进制,而是要按着IEEE 754标准才可以,
计算机语言恰好就是根据IEEE 754标准转化成十进制给用户看的。
那么根据IEEE 754标准比较大小:
10011001100110011001101
不等于(是大于)
00111111110110011001100110011001
10011001100110011001100110011010
当计算机语言,比如Java语言,把
10011001100110011001101
转化为十进制(根据IEEE 754标准,假如保留小数点20位):
有效数字是7-8位
(从左边第一个非零数字算起),但从第9位开始不保证有效!
把
00111111110110011001100110011001
10011001100110011001100110011010
转化为十进制(假如保留小数点20位):
有效数字是15-16位.
请阅读代码
运行效果如下图
如果您刚学第2章,肯定不能读懂全部代码,但可以运行它,尽量理解它(就像朋友相处的道理一样)。但是,如果您学完第4章,您也就能写出这样的代码,解答自己心中的疑惑! 比如,Float类的static方法
int floatToRawIntBits(float value)
是根据 IEEE 754 标准的位布局,返回指定浮点值value的IEEE 754 标准的表示形式。
注意:Java语言在输出正浮点数的内存形式时,就不输出符号位0,如果同时指数也是正数,也省略输出指数的符号位(比如对于float,输出的往往是30-31位,对于double,输出的往往是30-31位,对于double,输出的往往是62-63位)。
public class E {
public static void main(String args[]) {
float x = -0.4f;
double y = -0.4;
int bitInt = Float.floatToIntBits(x);
(Integer.toBinaryString(bitInt));
long bitLong = Double.doubleToLongBits(y);
(Long.toBinaryString(bitLong));
("%20.20f\n",
Float.intBitsToFloat(bitInt));
("%20.20f\n",
Double.longBitsToDouble(bitLong));
}
}