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
这样的数字,虽然很小,还不超过2
,这种情况下幂数完全满足要求,但是尾数已经不能表示出来了这么长的精度。
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
类型来转换。
分享到:
相关推荐
16进制单精度(32位)浮点型转换器,含有大端(左高位)、小端(右高位)
这个问题其实不是JAVA的bug,因为计算机本身是二进制的,而浮点数实际上只是个近似值,所以从二进制转化为十进制浮点数时,精度容易丢失,导致精度下降。 要保证精度就要使用BigDecimal类,而且不能直接从double...
在基本数据类型中,float和double都表示浮点型数据,而计算机计算采取的是对二进制的计算,所以会存在一定程度上的精度丢失问题。 BigDecimal类是一个大小数操作类,可以用来对超过16位有效位的数据进行精确的运算,...
主要介绍了Java POI读取excel中数值精度损失问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
JS 数字精度丢失的一些典型问题 JS 数字精度丢失的原因 解决方案(一个对象+一个函数) 一、JS数字精度丢失的一些典型问题 1. 两个简单的浮点数相加 0.1 + 0.2 != 0.3 // true 这真不是 Firebug 的问题,可以用...
NULL 博文链接:https://assassinme.iteye.com/blog/2369297
在进行浮点数运算时,可能会出现精度丢失的问题,例如0.1+0.2的结果并不等于0.3,而是近似于0.30000000000000004。 数据库字段类型选择 在设计数据库表结构时,需要对涉及小数的字段选择合适的数据类型,如DECIMAL...
Java的 的8⼤基本数据类型 ⼤基本数据类型 Java8⼤基本数据类型 ⼤基本数据类型 byte(字节型)、short(短整型)、int (整型) 、long(长整型) float(单精度浮点型)、double(双精度浮点型)、boolean(布尔型)、...
java运算工具类,可以处理浮点数的精度丢失问题,可以实现精确计算,常用公共方法抽取,常用工具类11111111111111111111111111111111111111111111111111111111111111111111111111
//需要解决的问题,数学的运算都有正负号的出现,在点击等号的时候就会有冲突,应该怎样解决,经验:双精度浮点型数据类型是会像后减一位。0.7会显示成0.69999999 public class app74 { static int i=0; static ...
注意:默认类型转换(自动类型提升)会丢失精度,但只有三种情况: int>float; long>float; long>double. 看一下他们的有效位就明白。 二进制是无法精确的表示 0.1 的。 进行高精度运算可以用java.math包中...
导入结果会丢失精度问题优化更多操作按钮左侧移入内容闪现消失情况修复主子表提交中列隐藏后出现列偏移问题单据打印网页时通过hidden-print隐藏元素表格销毁清除记住选择数据增加表格动态列示例代码生成选择主子表...
n(-84~127) 可以存储正数、负数、零、定点数和精度为38位的浮点数,其中,M表示精度,代表数字的总位数;N表示小数点右边数字的位数 日期类型 date 7字节 用于存储表中的日期和时间数据,取值范围是公元前4712年1月...