标题:
Java浮点数为什么精度会丢失
[打印本页]
作者:
qingqing3721
时间:
2011-5-1 09:22
标题:
Java浮点数为什么精度会丢失
由于对float或double 的运用不当, 能够会呈现精度丧失的效果。 效果大概情况可以通过如下代码了解:
Java代码
public class FloatDoubleTest {
public static void main(String[] args) {
float f = 20014999;
double d = f;
double d2 = 20014999;
System. out. println("f=" + f);
System. out. println("d=" + d);
System. out. println("d2=" + d2);
}
}
得到的后果如下:
f=2. 0015E7
d=2. 0015E7
d2=2. 0014999E7
从输出后果可以看出double 可以正确的表示20014999 , 而float 没有方法表示20014999 , 得到的只是一个近似值。 这样的后果很让人讶异。 20014999 这么小的数字在float下没方法表示。 带着这个效果, 一起学习一下浮点数, 做个简单分享, 希望有助于大家对java 浮点数的了解。
1. 关于 java 的 float 和 double 的表示法
Java 语言支持两种根本的浮点类型: float 和 double 。 java 的浮点类型都根据 IEEE 754 标准。 IEEE 754 定义了32 位和 64 位双精度两种浮点二进制小数标准。
IEEE 754 用迷信记数法以底数为 2 的小数来表示浮点数。
对于32 位浮点数float用 第1 位表示数字的符号, 用第2至9位来表示指数, 用 最初23 位来表示尾数, 即小数部分。
float(32位):
对于64 位双精度浮点数, 用 第1 位表示数字的符号, 用 11 位表示指数, 52 位表示尾数。
double(64位):
都是分为三个部分:
(1) 一个独自的符号位s 直接编码符号s 。
(2)k 位的幂指数E , 移码表示 。
(3)n 位的小数, 原码表示 。
2. 什么时分会呈现无法表示?
任何一个数字, 在java底层表示都必须转换成这种迷信计数法来表示, 那么我们来想想看什么时分这个数字会无法表示呢?那么只要两种情形:
1. 幂数不够表示了:这种情况往往呈现在数字太大了, 超越幂数所能承受的范围, 那么这个数字就无法表示了。 如幂数最大只能是10, 但是这个数字用迷信计数法表示时, 幂数一定会超越10, 就没方法了。
2. 尾数不够表示了:这种情况往往呈现在数字精度太长了, 如1. 3434343233332这样的数字, 虽然很小??共怀??, 这种情况下幂数完全满足要求, 但是尾数已经不能表示出来了这么长的精度。
3. 20014999 为什么用 float 没有方法正确表示?
通过以上剖析, 应该已经知道, 这个数字不大, 转换成IEEE754迷信计数法之后幂数一定是满足要求的, 只是尾数不能表示这么精确的数字了。
结合 float和double的表示方法, 通过剖析 20014999 的二进制表示就可以知道答案了。
以下程序可以得出 20014999 在 double 和 float 下的二进制表示方式。
Java代码
public class FloatDoubleTest3 {
public static void main(String[] args) {
double d = 20014999;
long l = Double. doubleToLongBits(d);
System. out. println(Long. toBinaryString(l));
float f = 20014999;
int i = Float. floatToIntBits(f);
System. out. println(Integer. toBinaryString(i));
}
}
输出后果如下:
Double:100000101110011000101100111100101110000000000000000000000000000
Float:1001011100110001011001111001100
对于输出后果剖析如下。 对于都不 double 的二进制右边补上符号位 0 刚好可以得到 64 位的二进制数。 根据double的表示法, 分为符号数、幂指数和尾数三个部分如下:
0 10000010111 0011000101100111100101110000000000000000000000000000
对于 float 右边补上符号位 0 刚好可以得到 32 位的二进制数。 根据float的表示法, 也分为 符号数、幂指数和尾数三个部分如下 :
0 10010111 00110001011001111001100
绿色部分是符号位, 红色部分是幂指数, 蓝色部分是尾数。
对比可以得出:
符号位都是 0 。
幂指数为移码表示, 两者刚好也相等。
独一不同的是尾数。
在 double 的尾数为: 001100010110011110010111 0000000000000000000000000000 , 省略后面的零, 至少需求24位才干正确表示 。
而在 float 下面尾数为: 00110001011001111001100 , 共 23 位。
为什么会这样?原因很明显, 因为 float 尾数 最多只能表示 23 位, 所以 24 位的 001100010110011110010111 在 float 下面经过四舍五入变成了 23 位的 00110001011001111001100 。 所以 20014999 在 float 下面变成了 20015000 。
也就是说 20014999 虽然是在float的表示范围之内, 但 在 IEEE 754 的 float 表示法精度长度没有方法表示出 20014999 , 而只能通过四舍五入得到一个近似值。
小结
浮点运算很少是精确的, 只需是超越精度能表示的范围就会产生误差。 往往产生误差不是因为数的大小???且蛭??木?取?因此, 产生的后果接近但不等于想要的后果。 尤其在运用 float 和 double 作精确运算的时分要特别小心。
可以考虑采用一些替代方案来实现。 如通过 String 结合 BigDecimal 或者通过运用 long 类型来转换。文章由 yusutag.tk 御淑堂丰胸产品 整理,收集辛苦,希望能保留出处。
欢迎光临 编程开发论坛 (http://bbs.lihuasoft.net/)
Powered by Discuz! 6.0.0