在java中运行一下代码
System.out.println(2.00-1.10);
输出的结果是:0.8999999999999999
很奇怪,并不是我们想要的值0.9

再运行如下代码:
System.out.println(2.00f-1.10f);
输出结果:0.9

又正确了,为什么会导致这种问题?程序中为什么要尽量避免浮点数比较?

在java中浮点型默认是double的,及2.00和1.10都要在计算机里转换进行二进制存储,这就涉及到数据精度,出现这个现象的原因正是浮点型数据的精度问题。先了解下float、double的基本知识:
1. float和double是java的基本类型,用于浮点数表示,在java中float占4个字节32位,double占8个字节64位,一般比较适合用于工程测量计算中,其在内存里的存储结构如下:float:

 符号位(1 bit) 指数(8 bit) 尾数(23 bit)

double:

符号位(1 bit) 指数(11 bit) 尾数(52 bit)

注意:从左到右是从低位到高位,而在计算机内部是采用逆序存储的。

2. System.out.println(2.00-1.10);中的1.10不能被计算机精确存储,以double类型数据1.10举例计算机如何将浮点型数据转换成二进制存储,
这里重点讲小数部分转换成二进制:
1.10整数部分就是1,转换成二进制1(这里整数转二进制不再赘述)
小数部分:0.1
0.1*2=0.2取整数部分0,基数=0.2
0.2*2=0.4取整数部分0,基数=0.4
0.4*2=0.8取整数部分0,基数=0.8
0.8*2=1.6取整数部分1,基数=1.6-1=0.6
0.6*2=1.2取整数部分1,基数=1.2-1=0.2
0.2*2=0.4取整数部分0,基数=0.4
.
.
.
.
直至基数为0。1.1用二进制表示为:1.000110...xxxx....(后面表示省略)
0.1 = 0*2^(-1)+0*2^(-2)+0*2^(-3)+1*2^(-4)+.........而double类型表示小数部分只有52位,当向后计算52位后基数还不为0,那后面的部分只能舍弃,从这里可以看出float、double并不能准确表示每一位小数,对于有的小数只能无限趋向它(所以有的数运行正常,有的数不正常)。在计算机中加减成除运算实际上最后都要在计算机中转换成二进制的加运算,由此,当计算机运行System.out.println(2.00-1.10);
时会拿他们在计算机内存中的二进制表示计算,而1.10的二进制表示本身就不准确,所以会出现0.8999999999999999的结果。

但为什么System.out.println(2.00f-1.10f);得出的结果是0.9呢。因为float精度没有double精度那么大,小数部分0.1二进制表示被舍去的比较多。

但是这不意味着float就是准确的 ,比如

float f = 1.0f - 0.9f;
System.out.println(f)

运行结果为0.100000024

因此,有个原则:

    • 程序中应尽量避免浮点数的比较
    • float、double类型的运算往往都不准确

那该如何表示一个精准的

在《Effective Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用java.math.BigDecimal。

BigDecimal一共有4个够造方法,我们不关心用BigInteger来够造的那两个,那么还有两个,它们是:
BigDecimal(double val) 
          Translates a double into a BigDecimal. 
BigDecimal(String val) 
          Translates the String repre sentation of a BigDecimal into a BigDecimal.
     上面的API简要描述相当的明确,而且通常情况下,上面的那一个使用起来要方便一些。我们可能想都不想就用上了,会有什么问题呢?等到出了问题的时候,才发现上面哪个够造方法的详细说明中有这么一段:
Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances nonwithstanding. 
The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one.
原来我们如果需要精确计算,非要用String来够造BigDecimal不可!在《Effective Java》一书中的例子是用String来够造BigDecimal的,但是书上却没有强调这一点,这也许是一个小小的失误吧。

BigDecimal bg1 = new BigDecimal(0.9);
BigDecimal bg2 = new BigDecimal(1.0);
System.out.println(bg2.subtract(bg1));//输出0.09999999999999997779553950749686919152736663818359375

BigDecimal bg3= new BigDecimal("0.9");
BigDecimal bg4 = new BigDecimal("1.0");
System.out.println(bg4.subtract(bg3));//输出0.1

解决方案
现在我们已经可以解决这个问题了,原则是使用BigDecimal并且一定要用String来够造。

java float、double精度研究(转)的更多相关文章

  1. java float double精度为什么会丢失?浅谈java的浮点数精度问题 【转】

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

  2. 解决java float double 浮点型参与计算失精度

    本人前段时间做一个社区电商应用,发现了一个 天坑   ...................让我哭会 . 下面听听我的踩坑之路吧 ,电商肯定跟¥打交道了,计算少不了的.由于本人太菜 单纯的以为  fl ...

  3. java float double bigdecimal

    java 有 float,double,BigDecimal 三种,前两者会损失精度,最后一个是专门用于高精度计算的大数类型,但是会损失性能.如果用于金融场合且小数位并不多的时候,可以考虑 BigDe ...

  4. JAVA float double数据类型保留2位小数点5种方法

    /** * Java 两个整数相除保留两位小数,将小数转化为百分数 * java中,当两个整数相除时,由于小数点以后的数字会被截断,运算结果将为整数,此时若希望得到运算结果为浮点数,必须将两整数其一或 ...

  5. Java面试官:兄弟,你确定double精度比float低吗?

    我有一个朋友,叫老刘,戴着度数比我还高的近视镜,显得格外的"程序员":穿着也非常"不拘一格",上半身是衬衣西服,下半身是牛仔裤运动鞋. 我和老刘的感情非常好,每 ...

  6. java用double和float进行小数计算精度不准确

    java用double和float进行小数计算精度不准确 大多数情况下,使用double和float计算的结果是准确的,但是在一些精度要求很高的系统中或者已知的小数计算得到的结果会不准确,这种问题是非 ...

  7. Java中float/double取值范围与精度

    Java浮点数 浮点数结构 要说清楚Java浮点数的取值范围与其精度,必须先了解浮点数的表示方法,浮点数的结构组成,之所以会有这种所谓的结构,是因为机器只认识01,你想表示小数,你要机器认识小数点这个 ...

  8. java float与double的范围和精度

    float与double的范围和精度 1. 范围  float和double的范围是由指数的位数来决定的.  float的指数位有8位,而double的指数位有11位,分布如下:  float:  1 ...

  9. [ JAVA编程 ] double类型计算精度丢失问题及解决方法

    前言 如果你在测试金融相关产品,请务必覆盖交易金额为小数的场景.特别是使用Java语言的初级开发. Java基本实例 先来看Java中double类型数值加.减.乘.除计算式实例: public cl ...

随机推荐

  1. 函数 flst_get_first

    /********************************************************************//** Gets list first node addre ...

  2. JavaScript里的类和继承

    JavaScript与大部分客户端语言有几点明显的不同: JS是 动态解释性语言,没有编译过程,它在程序运行过程中被逐行解释执行JS是 弱类型语言,它的变量没有严格类型限制JS是面向对象语言,但 没有 ...

  3. web测试中,各类web控件测试点总结

    一 .界面检查 进入一个页面测试,首先是检查title,页面排版,字段等,而不是马上进入文本框校验 1.页面名称title是否正确 2.当前位置是否可见  您的位置:xxx>xxxx 3.文字格 ...

  4. 归并排序 求逆序数 链表的归并排序 多线程归并排序 java

    import java.util.Scanner; public class Main { private static int count=0; public static void mergeso ...

  5. getchar

    getchar函数每次只从缓冲区中接受一个字符. getchar有一个int型的返回值. 当程序调用getchar时,程序就等着用户按键,用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字 ...

  6. bzoj 1880 [Sdoi2009]Elaxia的路线(最短路+拓扑序)

    Description 最近,Elaxia和w**的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们 必须合理地安排两个人在一起的时间.Elaxia和w**每天都要奔波于宿舍和实验室之间, ...

  7. NOIP2002 字串变换

    题二 字串变换 (存盘名: NOIPG2) [问题描述]: 已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ -> B2$ 规则的含义为: ...

  8. 12款有助于简化CSS3开发的工具

    网站开发者能通过CSS3为网站设计增添很多时尚元素,CSS3 对CSS规范做了很大的改进.现在,本文将介绍12款有助于简化CSS3开发的工具.  1.CSS3 Pie: 允许在IE上使用CSS3绝大部 ...

  9. 路由器中pppoe,动态IP,静态IP的区别

    路由器中pppoe,动态IP,静态IP的区别 要把路由器设置得能上网,无非就是设置WAN外网接口连接而已.WAN接口能上网,则连接的电脑就能上网,反之则上不了网.只不过WAN接口往往有pppoe,动态 ...

  10. A Tour of Go Nil slices

    The zero value of a slice is nil. A nil slice has a length and capacity of 0. (To learn more about s ...