两个double的变量相加,结果却出乎意料之外。例如:

double A = 20, B = 10.26;

double C = A + B = 30.25999999998;

为什么会出现这个结果,参考了很多网友的答案,原因应该是“2进制和16进制转换的时候的偏度差造成的”。在处理财务相关的数据时,这样的N次偏差可能会使结果完全不可取。

改正的方法是,在执行加运算前,先将A执行ToString,再ToBigDecimal,执行BigDecimal.Add运算之后再返回BigDecimal.doubleValue。(备注,后来发现在.net里面没有类BigDecimal,这是java下的类型)

这个问题相当严重,如果你有9.999999999999元,你的计算机是不会认为你可以购买10元的商品的。

在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。现在让我们看看如何解决这个问题。

四舍五入

我们的第一个反应是做四舍五入。Math类中的round方法不能设置保留几位小数,我们只能象这样(保留两位):

public double round(double value){

return Math.round(value*100)/100.0;

}

非常不幸,上面的代码并不能正常工作,给这个方法传入4.015它将返回4.01而不是4.02,如我们在上面看到的

4.015*100=401.49999999999994

因此如果我们要做到精确的四舍五入,不能利用简单类型做任何运算

java.text.DecimalFormat也不能解决这个问题:

System.out.println(new java.text.DecimalFormat("0.00").format(4.025));

输出是4.02

BigDecimal 
在《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并且一定要用String来够造。

但是想像一下吧,如果我们要做一个加法运算,需要先将两个浮点数转为String,然后够造成BigDecimal,在其中一个上调用add方法,传入另一个作为参数,然后把运算的结果(BigDecimal)再转换为浮点数。你能够忍受这么烦琐的过程吗?下面我们提供一个工具类Arith来简化操作。它提供以下静态方法,包括加减乘除和四舍五入:

public static double add(double v1,double v2)

public static double sub(double v1,double v2)

public static double mul(double v1,double v2)

public static double div(double v1,double v2)

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

public static double round(double v,int scale)

附录

源文件Arith.java:

package com.common.util; 
import java.math.BigDecimal; 
/** 
* 由于Java的简单类型不能够精确的对浮点数进行运算, 
* 这个工具类提供精确的浮点数运算,包括加减乘除和四舍五入。 
*/ 
public final class Arith { 
// 默认除法运算精度 
private static final int DEF_DIV_SCALE = 2; 
// 这个类不能实例化 
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(); 

}

转载于:https://www.cnblogs.com/icyJ/archive/2013/04/26/3045310.html

jave double相加结果误差+尾巴相关推荐

  1. 两个Double相加,小数点有很多位问题

    在我们进行两个double运算时,例如:2-0-1.1 不是想象的输出0.9,而是0.89999999999999999.其主要原因是浮点数值采用二进制系统表示,而在二进制系统中无法精确的表示分数1/ ...

  2. float 最小误差_关于float与double结果的误差

    float a;a=123456.789e5;- s* ~' V9 N; u! H0 x# RSystem.out.printf("%f\n",a);* s- l4 t. Q2 [ ...

  3. java double相加_解决java中两个double类型的值相加

    代码: /** * 提供精确的加法运算 * @param v1 被加数 * @param v2 加数 * @return 两个参数的和 */ public static double add(doub ...

  4. C++类型转换实现不同类型相加【复数与double类型相加】

    文章目录 一.现double数据与Complex类型相加,得出double 二.复数与double相加,得出复数 一.现double数据与Complex类型相加,得出double #include & ...

  5. Java中float和double精度

    http://zhidao.baidu.com/question/344295417.html?seed=0 float与double的区别 单精度浮点数在机内占4个字节,用32位二进制描述. 双精度 ...

  6. java double丢失精度问题,加减乘除计算出错出现99999

    问题一 原理:在使用double计算时,经常会存在精度丢失,总是在一个正确的结果左右偏0.0000**1,经常会因为精度丢失而导致程序处理流程出错.所有在进行财务相关计算时,需要使用BigDecima ...

  7. 线上故障之-雪花算法重复、序列化、redis锁失效、double精准计算

    线上故障之-雪花算法重复.序列化.redis锁失效.double精准计算 雪花算法重复 问题发现 定位 影响范围 解决方案 扩展-雪花算法原理分析: 序列化 运营商 POP 裸机搭售自营套餐事故分析 ...

  8. Java中double类型精度丢失的问题_double类型数据加减操作精度丢失解决方法_BigDecimal取整

    BigDecimal在用double做入参的时候,二进制无法精确地表示十进制小数,编译器读到字符串"0.0000002"和"1.0000002"之后,必须把它转 ...

  9. 系统学习深度学习(六) --LSTM总结

    转自:http://blog.csdn.net/shincling/article/details/49362161 1. 概念: Long short-termmemory (LSTM)is a r ...

  10. Java算法:华为机试算法(下),华为算法Java版,牛客网华为算法73~108题

    接上篇:Java算法:华为机试算法(中),华为算法Java版,牛客网华为算法55~72题   HJ73 计算日期到天数转换 计算日期到天数转换 题目描述 根据输入的日期,计算是这一年的第几天.. 测试 ...

最新文章

  1. 手动添加Cookie
  2. 怎么才能领取到外卖红包呢
  3. Qt学习(四):qt读写文件
  4. 在eclipse中修改java web项目的名称
  5. java 实例变量和类变量
  6. php 查询sybase 实例,php访问sybase16
  7. 小米畅聊也将瞄准镜对向微信电话本
  8. torch.distributed多卡/多GPU/分布式DPP(二)—torch.distributed.all_reduce(reduce_mean)barrier控制进程执行顺序seed随机种子
  9. 获取 IE 浏览器版本以及判断是否开启 cookie
  10. NPOI Word 换行 【XWPFDocument】
  11. VBA金额转换中文大写(原创新解版)
  12. 风扇空调照明灯动画的创建
  13. UE4-目录结构简介
  14. 蘑菇云「行空板Python入门教程」第七课:舒尔特方格小游戏
  15. 语法树的Java代码自动化插桩
  16. ipam:allocate ip 172.16.3.7 to node x.x.x.112 allocator failed, provided IP is already allocated
  17. 【拜小白opencv】45-二维H-S直方图绘制----calcHist()函数、minMaxLoc()函数
  18. ActiveMQ 源码学习 2:从 CommandTypes 谈常量接口反模式
  19. 流程发现算法第1讲| Alpha算法
  20. Ubuntu安装星际词典

热门文章

  1. paip.c++ 进程间通信方法大总结
  2. VSCode: 快捷键大全
  3. 曹 雷 : 证券基金经营机构如何理解科技是投资而非投入
  4. (转)金融“核武器”即将引爆整个行业
  5. php 从1累加到50,javascript - 一个数每隔一秒执行加1并打印出来,一直加到50停止,用js如何实现?...
  6. 【语音去噪】基于matlab谱减法去噪【含Matlab源码 429期】
  7. 【VRP】基于matlab改进的模拟退火和遗传算法求解车辆路径规划问题【含Matlab源码 343期】
  8. 涉密计算机多用户审批表,涉密计算机及信息系统安全策略
  9. 技术专家(ai/大数据)_``我们淹没在数据中'':在专家和AI时代如何思考自己
  10. 通话时自动中断音乐播放_您知道用户在何处以及为何中断通话吗?