Java double和 float丢失精度问题

简介:

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


  1. public class FloatDoubleTest {  

  2. public static void main(String[] args) {  

  3. float f = 20014999;  

  4. double d = f;  

  5. double d2 = 20014999;  

  6. System.out.println("f=" + f);  

  7. System.out.println("d=" + d);  

  8. System.out.println("d2=" + d2);  

  9. }  

  10. }  


得到的结果如下:

f=2.0015E7

d=2.0015E7

d2=2.0014999E7

从输出结果可以看出double 可以正确的表示20014999 ,而float 没有办法表示20014999 ,得到的只是一个近似值。这样的结果很让人讶异。20014999 这么小的数字在float下没办法表示。于是带着这个问 题,做了一次关于float和double学习,做个简单分享,希望有助于大家对java 浮 点数的理解。

 

关于 java  float  double

Java 语言支持两种基本的浮点类型: float 和 double java 的浮点类型都依据 IEEE 754 标准。IEEE 754 定义了32 位和 64 位双精度两种浮点二进制小数标准。

IEEE 754 用科学记数法以底数为 的小数来表示浮点数。32 位浮点数用 位表示数字的符号,用 位来表示指数,用 23 位来表示尾数,即小数部分。作为有符号整数的指数可以有正负之分。小数部分用二进制(底数 2 )小数来表示。对于64 位双精度浮点数,用 位表示数字的符号,用 11 位表示指数,52 位表示尾数。如下两个图来表示:

float(32位):

float

double(64位):

double

都是分为三个部分:

(1) 一 个单独的符号位s 直接编码符号s 。

(2)k 位 的幂指数E ,移 码表示 

(3)n 位 的小数,原码表示 

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

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

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


  1. public class FloatDoubleTest3 {  

  2. public static void main(String[] args) {  

  3. double d = 8;  

  4. long l = Double.doubleToLongBits(d);  

  5. System.out.println(Long.toBinaryString(l));  

  6. float f = 8;  

  7. int i = Float.floatToIntBits(f);  

  8. System.out.println(Integer.toBinaryString(i));  

  9. }  

  10. }  


输出结果如下:

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 类型来转换。


特别说明:尊重作者的劳动成果,转载请注明出处哦~~~http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt357
相关文章
|
8月前
|
存储 Java
百度搜索:蓝易云【Java语言之float、double内存存储方式】
由于使用IEEE 754标准进行存储,float和double类型可以表示非常大或非常小的浮点数,并且具有一定的精度。然而,由于浮点数的特性,它们在进行精确计算时可能会存在舍入误差。在编写Java程序时,需要注意使
104 0
|
2月前
|
Java
Java项目中高精度数值计算:为何BigDecimal优于Double
在Java项目开发中,涉及金额计算、面积计算等高精度数值操作时,应选择 `BigDecimal` 而非 `Double`。`BigDecimal` 提供任意精度的小数运算、多种舍入模式和良好的可读性,确保计算结果的准确性和可靠性。例如,在金额计算中,`BigDecimal` 可以精确到小数点后两位,而 `Double` 可能因精度问题导致结果不准确。
|
5月前
|
Java
【Java基础面试十】、何对Integer和Double类型判断相等?
这篇文章讨论了如何在Java中正确比较`Integer`和`Double`类型的值,指出不能直接使用`==`操作符比较不同类型,而应该将它们转换为相同的基本数据类型(如`double`)后进行比较。
【Java基础面试十】、何对Integer和Double类型判断相等?
|
6月前
|
Java
Java中将保留四位小数的Double转换为String的方法详解
选择合适的方法,可以使代码更加简洁、高效,同时也能满足不同场景下的需求。
127 5
|
7月前
|
Java
Java的double值保留2位小数
【6月更文挑战第16天】Java的double值保留2位小数
258 0
|
8月前
|
Java 定位技术
在Java中为什么不推荐使用Float
在Java中为什么不推荐使用Float
100 1
|
8月前
|
存储 编译器 C语言
魔性的float浮点数精度问题
魔性的float浮点数精度问题
71 0
|
Java
在java中,怎样把一个double数转换为字符串时,不用科学计数法表示。
在java中,怎样把一个double数转换为字符串时,不用科学计数法表示。
562 0
|
6天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
44 17
|
16天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者