浅谈java的浮点数精度问题及如何解决精度缺失问题.docx

上传人:b****6 文档编号:7150476 上传时间:2023-01-21 格式:DOCX 页数:7 大小:22.37KB
下载 相关 举报
浅谈java的浮点数精度问题及如何解决精度缺失问题.docx_第1页
第1页 / 共7页
浅谈java的浮点数精度问题及如何解决精度缺失问题.docx_第2页
第2页 / 共7页
浅谈java的浮点数精度问题及如何解决精度缺失问题.docx_第3页
第3页 / 共7页
浅谈java的浮点数精度问题及如何解决精度缺失问题.docx_第4页
第4页 / 共7页
浅谈java的浮点数精度问题及如何解决精度缺失问题.docx_第5页
第5页 / 共7页
点击查看更多>>
下载资源
资源描述

浅谈java的浮点数精度问题及如何解决精度缺失问题.docx

《浅谈java的浮点数精度问题及如何解决精度缺失问题.docx》由会员分享,可在线阅读,更多相关《浅谈java的浮点数精度问题及如何解决精度缺失问题.docx(7页珍藏版)》请在冰豆网上搜索。

浅谈java的浮点数精度问题及如何解决精度缺失问题.docx

浅谈java的浮点数精度问题及如何解决精度缺失问题

javafloatdouble精度为什么会丢失?

浅谈java的浮点数精度问题

由于对float或double的使用不当,可能会出现精度丢失的问题。

问题大概情况可以通过如下代码理解:

[java] viewplaincopyprint?

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. System.out.println(0.05+0.01);  

10. System.out.println(1.0-0.42);  

11. System.out.println(4.015*100);  

12. System.out.println(123.3/100); 

13.}  

14.}  

publicclassFloatDoubleTest{publicstaticvoidmain(String[]args){floatf=20014999;doubled=f;doubled2=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下没办法表示。

于是带着这个问题,做了一次关于float和double学习,做个简单分享,希望有助于大家对java浮点数的理解。

 

关于java的float和double

Java语言支持两种基本的浮点类型:

float和double。

java的浮点类型都依据IEEE754标准。

IEEE754定义了32位和64位双精度两种浮点二进制小数标准。

IEEE754用科学记数法以底数为2的小数来表示浮点数。

32位浮点数用1位表示数字的符号,用8位来表示指数,用23位来表示尾数,即小数部分。

作为有符号整数的指数可以有正负之分。

小数部分用二进制(底数2)小数来表示。

对于64位双精度浮点数,用1位表示数字的符号,用11位表示指数,52位表示尾数。

如下两个图来表示:

float(32位):

double(64位):

都是分为三个部分:

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

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

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

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

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

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

[java] viewplaincopyprint?

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.}  

publicclassFloatDoubleTest3{publicstaticvoidmain(String[]args){doubled=8;longl=Double.doubleToLongBits(d);System.out.println(Long.toBinaryString(l));floatf=8;inti=Float.floatToIntBits(f);System.out.println(Integer.toBinaryString(i));}}

输出结果如下:

Double:

100000101110011000101100111100101110000000000000000000000000000

Float:

1001011100110001011001111001100

对于输出结果分析如下。

对于都不double的二进制左边补上符号位0刚好可以得到64位的二进制数。

根据double的表示法,分为符号数、幂指数和尾数三个部分如下:

0100000101110011000101100111100101110000000000000000000000000000

对于float左边补上符号位0刚好可以得到32位的二进制数。

根据float的表示法,也分为符号数、幂指数和尾数三个部分如下:

01001011100110001011001111001100

绿色部分是符号位,红色部分是幂指数,蓝色部分是尾数。

对比可以得出:

符号位都是0,幂指数为移码表示,两者刚好也相等。

唯一不同的是尾数。

在double的尾数为:

0011000101100111100101110000000000000000000000000000,省略后面的零,至少需要24位才能正确表示。

而在float下面尾数为:

00110001011001111001100,共23位。

为什么会这样?

原因很明显,因为float尾数最多只能表示23位,所以24位的001100010110011110010111在float下面经过四舍五入变成了23位的00110001011001111001100。

所以20014999在float下面变成了20015000。

也就是说20014999虽然是在float的表示范围之内,但在IEEE754的float表示法精度长度没有办法表示出20014999,而只能通过四舍五入得到一个近似值。

 

总结:

浮点运算很少是精确的,只要是超过精度能表示的范围就会产生误差。

往往产生误差不是因为数的大小,而是因为数的精度。

因此,产生的结果接近但不等于想要的结果。

尤其在使用float和double作精确运算的时候要特别小心。

可以考虑采用一些替代方案来实现。

如通过String结合BigDecimal或者通过使用long类型来转换。

解决方案:

packageA;

 

import java.math.BigDecimal; 

/** 

  * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 

  * 确的浮点数运算,包括加减乘除和四舍五入。

 

  */ 

  

public class Arith{ 

  

     //默认除法运算精度 

     private static final int DEF_DIV_SCALE = 10; 

  

  

     //这个类不能实例化 

     private Arith(){ 

     } 

  

   

     /** 

      * 提供精确的加法运算。

 

      * @param v1 被加数 

      * @param v2 加数 

      * @return 两个参数的和 

      */ 

  

     public static double add(double v1,double v2){ 

         BigDecimal b1 = new BigDecimal(Double.toString(v1)); 

         BigDecimal b2 = new BigDecimal(Double.toString(v2)); 

         return b1.add(b2).doubleValue(); 

     } 

  

     /** 

      * 提供精确的减法运算。

 

      * @param v1 被减数 

      * @param v2 减数 

      * @return 两个参数的差 

      */ 

  

     public static double sub(double v1,double v2){ 

         BigDecimal b1 = new BigDecimal(Double.toString(v1)); 

         BigDecimal b2 = new BigDecimal(Double.toString(v2)); 

         return b1.subtract(b2).doubleValue(); 

     }  

  

     /** 

      * 提供精确的乘法运算。

 

      * @param v1 被乘数 

      * @param v2 乘数 

      * @return 两个参数的积 

      */ 

  

     public static double mul(double v1,double v2){ 

         BigDecimal b1 = new BigDecimal(Double.toString(v1)); 

         BigDecimal b2 = new BigDecimal(Double.toString(v2)); 

         return b1.multiply(b2).doubleValue(); 

     } 

  

   

  

     /** 

      * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 

      * 小数点以后10位,以后的数字四舍五入。

 

      * @param v1 被除数 

      * @param v2 除数 

      * @return 两个参数的商 

      */ 

  

     public static double div(double v1,double v2){ 

         return div(v1,v2,DEF_DIV_SCALE); 

     } 

  

   

  

     /** 

      * 提供(相对)精确的除法运算。

当发生除不尽的情况时,由scale参数指 

XX文库-让每个人平等地提升自我      * 定精度,以后的数字四舍五入。

 

      * @param v1 被除数 

      * @param v2 除数 

      * @param scale 表示表示需要精确到小数点以后几位。

 

      * @return 两个参数的商 

      */ 

  

     public static double div(double v1,double v2,int scale){ 

         if(scale<0){ 

             throw new IllegalArgumentException( 

                 "The scale must be a positive integer or zero"); 

         } 

         BigDecimal b1 = new BigDecimal(Double.toString(v1)); 

         BigDecimal b2 = new BigDecimal(Double.toString(v2)); 

         return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); 

     } 

  

   

  

     /** 

      * 提供精确的小数位四舍五入处理。

 

      * @param v 需要四舍五入的数字 

      * @param scale 小数点后保留几位 

      * @return 四舍五入后的结果 

      */ 

  

     public static double round(double v,int scale){ 

         if(scale<0){ 

             throw new IllegalArgumentException( 

                 "The scale must be a positive integer or zero"); 

         } 

         BigDecimal b = new BigDecimal(Double.toString(v)); 

         BigDecimal one = new BigDecimal("1"); 

         return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); 

     } 

}; 

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 社交礼仪

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1