java float double为什么精度会丢失

本文通过实例解释了在Java中使用float表示特定整数值时出现的精度问题,并深入探讨了其背后的原因,包括float和double如何在二进制中表示。

由于对float或double 的使用不当,可能会出现精度丢失的问题。问题大概情况可以通过如下代码理解:

 

public class Num
{
	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下没办法表示。

 

那么 20014999 为什么用 float 没有办法正确表示?

 

 

结合float和double的表示方法,通过分析 20014999 的二进制表示就可以知道答案了。

以下程序可以得出 20014999  double  float 下的二进制表示方式。

 

public class FloatDouble
{
	public static void main(String[] args)
	{
		double d = 8;
		long l = Double.doubleToLongBits(d);
		System.out.println(Long.toBinaryString(l));
		float f = 8;
		int i = Float.floatToIntBits(f);
		System.out.println(Integer.toBinaryString(i));
	}
}
 

 

输出结果如下:

 

Double:100000000100000000000000000000000000000000000000000000000000000

Float:1000001000000000000000000000000

对于输出结果分析如下。对于都不 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 类型来转换。

 

 

 

 

 

### Javafloatdouble类型产生精度缺失的原因 Java中的`float`和`double`类型遵循IEEE 754标准来表示浮点数[^1]。该标准定义了如何以二进制形式存储十进制浮点数,包括符号位、指数位和尾数位的分配。对于`float`类型,总共有32位,其中1位用于符号位,8位用于指数位,23位用于尾数位;而对于`double`类型,则有64位,其中1位用于符号位,11位用于指数位,52位用于尾数位[^2]。 #### 精度缺失的原因 精度缺失的根本原因在于十进制小数无法精确地用二进制表示。例如,十进制的小数0.1在二进制中是一个无限循环的小数,类似于十进制中的1/3=0.333...。由于计算机内存有限,无法存储无限位数的二进制小数,因此必须进行截断或舍入操作,这导致了精度丢失[^4]。 此外,`float`类型的尾数位仅有23位,有效位数大约为6-7位十进制数字,而`double`类型的尾数位有52位,有效位数约为15-16位十进制数字。这意味着`float`类型的精度较低,在处理较大数值或需要高精度计算时更容易出现误差[^2]。 以下代码示例展示了精度丢失的现象: ```java public class FloatDoubleTest { public static void main(String[] args) { float f = 20014999; double d = f; double d2 = 20014999; System.out.println("f=" + f); // 输出: f=2.0014998E7 System.out.println("d=" + d); // 输出: d=2.0014998E7 System.out.println("d2=" + d2); // 输出: d2=2.0014999E7 } } ``` 从输出结果可以看出,`float`类型的变量`f`在转换为`double`类型时,出现了精度丢失,这是因为`float`类型的存储能力不足以完全表示`20014999`这个值[^3]。 #### IEEE 754标准的作用 IEEE 754标准通过规范浮点数的存储格式,确保了不同系统之间的兼容性。尽管该标准能够很好地表示大多数浮点数,但由于二进制表示的局限性,某些特定的十进制小数仍然无法精确存储,从而导致精度丢失的问题[^1]。 ### 总结 Java中的`float`和`double`类型产生的精度缺失问题,主要源于IEEE 754标准下二进制对十进制小数的近似表示以及有限的存储位数。对于需要高精度的应用场景,可以考虑使用`BigDecimal`类来替代浮点数类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值