为什么会出现这个问题呢,就这是java和其它计算机语言都会出现的问题,下面我们分析一下为什么会出现这个问题:
float和double类型主要是为了科学计算和工程计算而设计的。他们执行二进制浮点运算,这是为了在广泛的数字范围上提供较为精确的快速近似计算而精心设计的。然而,它们并没有提供完全精确的结果,所以我们不应该用于精确计算的场合。float和double类型尤其不适合用于货币运算,因为要让一个float或double精确的表示0.1或者10的任何其他负数次方值是不可能的(其实道理很简单,十进制系统中能不能准确表示出1/3呢?同样二进制系统也无法准确表示1/10)。

浮点运算很少是精确的,只要是超过精度能表示的范围就会产生误差。往往产生误差不是因为数的大小,而是因为数的精度。因此,产生的结果接近但不等于想要的结果。尤其在使用 float 和 double 作精确运算的时候要特别小心。

现在我们就详细剖析一下浮点型运算为什么会造成精度丢失?

首先我们要搞清楚下面两个问题:

     (1) 十进制整数如何转化为二进制数

           算法很简单。举个例子,11表示成二进制数:

                     11/2=5 余   1

                       5/2=2   余   1

                       2/2=1   余   0

                       1/2=0   余   1

                          0结束         11二进制表示为(从下往上):1011

          这里提一点:只要遇到除以后的结果为0了就结束了,大家想一想,所有的整数除以2是不是一定能够最终得到0。换句话说,所有的整数转变为二进制数的算法会不会无限循环下去呢?绝对不会,整数永远可以用二进制精确表示 ,但小数就不一定了。

      (2) 十进制小数如何转化为二进制数

           算法是乘以2直到没有了小数为止。举个例子,0.9表示成二进制数

                     0.9*2=1.8   取整数部分 1

                     0.8(1.8的小数部分)*2=1.6    取整数部分 1

                     0.6*2=1.2   取整数部分 1

                     0.2*2=0.4   取整数部分 0

                     0.4*2=0.8   取整数部分 0

                     0.8*2=1.6 取整数部分 1

                     0.6*2=1.2   取整数部分 0

                              .........      0.9二进制表示为(从上往下): 1100100100100......

           注意:上面的计算过程循环了,也就是说*2永远不可能消灭小数部分,这样算法将无限下去。很显然,小数的二进制表示有时是不可能精确的 。其实道理很简单,十进制系统中能不能准确表示出1/3呢?同样二进制系统也无法准确表示1/10。这也就解释了为什么浮点型减法出现了"减不尽"的精度丢失问题。

解决方法

使用BigDecmal,而且需要在构造参数使用String类型。

在《Effective Java》这本书中就给出了一个解决方法。该书中也指出,float和double只能用来做科学计算或者是工程计算,在商业计算等精确计算中,我们要用java.math.BigDecimal。

BigDecimal类有4个构造方法,我们只关心对我们解决浮点型数据进行精确计算有用的方法,即

BigDecimal(double value) // 将double型数据转换成BigDecimal型数据

思路很简单,我们先通过BigDecimal(double value)方法,将double型数据转换成BigDecimal数据,然后就可以正常进行精确计算了。等计算完毕后,我们可以对结果做一些处理,比如 对除不尽的结果可以进行四舍五入。最后,再把结果由BigDecimal型数据转换回double型数据。

这个思路很正确,但是如果你仔细看看API里关于BigDecimal的详细说明,你就会知道,如果需要精确计算,我们不能直接用double,而非要用 String来构造BigDecimal不可!所以,我们又开始关心BigDecimal类的另一个方法,即能够帮助我们正确完成精确计算的 BigDecimal(String value)方法。

// BigDecimal(String value)能够将String型数据转换成BigDecimal型数据

那么问题来了,想像一下吧,如果我们要做一个浮点型数据的加法运算,需要先将两个浮点数转为String型数据,然后用 BigDecimal(String value)构造成BigDecimal,之后要在其中一个上调用add方法,传入另一个作为参数,然后把运算的结果(BigDecimal)再转换为浮 点数。如果每次做浮点型数据的计算都要如此,你能够忍受这么烦琐的过程吗?至少我不能。所以最好的办法,就是写一个类,在类中完成这些繁琐的转换过程。这 样,在我们需要进行浮点型数据计算的时候,只要调用这个类就可以了。网上已经有高手为我们提供了一个工具类Arith来完成这些转换操作。它提供以下静态 方法,可以完成浮点型数据的加减乘除运算和对其结果进行四舍五入的操作:

package com.util;
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参数指定精度,以后的数字四舍五入
* @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();
}
}

  

附上Arith的源代码,大家只要把它编译保存好,要进行浮点数计算的时候,在你的源程序中导入Arith类就可以使用以上静态方法来进行浮点数的精确计算了。

java中double和float精度丢失问题的更多相关文章

  1. java中double和float精度丢失问题及解决方法

    在讨论两位double数0.2和0.3相加时,毫无疑问他们相加的结果是0.5.但是问题总是如此吗? 下面我们让下面两个doubles数相加,然后看看输出结果: @Test public void te ...

  2. java防止double和float精度丢失的方法

    在浮点数当中做运算时经常会出现精度丢失的情况,如果做项目不作处理的话会对商家造成很大的影响的.项目尤其是金融相关的项目对这些运算的精度要求较高. 问题原因:首先计算机进行的是二进制运算,我们输入的十进 ...

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

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

  4. iOS项目double、float精度丢失解决办法

    描述 在iOS项目中老是遇到double.float精度丢失的问题 PS: NSString * jsonStr = @"{\"9.70\":9.70,\"67 ...

  5. [坑]c#中double转字符串精度丢失问题记录

    在项目遇到了一个比较大的double值,然后出现了一些意想不到的状况: double b=1141.161994934082; b.ToString();//'1141.16199493408' 然后 ...

  6. Java中浮点型数据Float和Double进行精确计算的问题

    Java中浮点型数据Float和Double进行精确计算的问题 来源  https://www.cnblogs.com/banxian/p/3781130.html 一.浮点计算中发生精度丢失     ...

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

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

  8. Java 中的浮点数取精度方法

    Java 中的浮点数取精度方法 一.内容 一般在Java代码中取一个double类型的浮点数的精度,四舍五入或者直接舍去等的方式,使用了4种方法,推荐使用第一种,我已经封装成工具类了. 二.代码实现 ...

  9. java中double变量保留小数问题

    (转载自玄影池扁舟) 做java项目的时候可能经常会遇到double类型变量保留小数的问题,下面便把我的经验做个简短的总结: java中double类型变量保留小数问题大体分两种情况: (一):小数点 ...

随机推荐

  1. 安装 nodejs,npm,pm2

    一:需要安装组件: nodejs,npm,pm2 安装epel 源: rpm -ivh http://download.fedoraproject.org/pub/epel/6/x86_64/epel ...

  2. maven库 mvn依赖

    http://maven.outofmemory.cn/ http://mvnrepository.com/ 先执行 mvn clean  然后执行  mvn 命令 如:mvn  compile  . ...

  3. Linux wc指令解析

    wc指令比较实用,可以统计文件中的字节数.字符数.行数.字数等. 先通过 wc --help 查看指令帮助. $ wc --help Usage: wc [OPTION]... [FILE]... o ...

  4. Android Studio failed to create error code -6解决方法

    起因是AndroidStudio编译太慢,在stackoverflow上找解决方法,创建了个vm选项文件,导致内存不够用 很多中文博客上都千篇一律地说是内存不够,打开安装路径下bin目录下的studi ...

  5. Java集合类综合

    Java集合类是JDK学习中的一个经典切入点,也是让初学者最初感受到Java魅力的地方之一,你一定不会忘记不需要关心大小的ArrayList,不用自己实现的Queue,和随处可见的HashMap.面试 ...

  6. post参数的方法 json data 和特别的传参

    json格式传参: 那么久使用json的方式传参: json=payload data格式传参: 其他方式传参: 在webFormes里 value 的值不是普通的字符 要把value值先序列化在放入 ...

  7. java wab----遇到经常用到集合list/map/

    List与Vector的区别: vector适用:对象数量变化少,简单对象,随机访问元素频繁 list适用:对象数量变化大,对象复杂,插入和删除频] List首先是链表,它的元素不是连续的.vecto ...

  8. Java 运用流传输文件

    实例1 package IO; import java.io.FileReader; import java.io.FileWriter; import java.io.Reader; import ...

  9. Bootstrap-Other:UI 编辑器

    ylbtech-Bootstrap-Other:UI 编辑器 1.返回顶部 1. Bootstrap UI 编辑器 以下是 15 款最好的 Bootstrap 编辑器或者是在线编辑工具. 1. Boo ...

  10. charles2 重写

    重写 重写功能可以重写对应的内容,主要对某些匹配请求的header.host.URL.path.query param.body.response等参数删除.修改.增加. 和断点相比,适合做长期和批量 ...