现在的位置: 首页 > 综合 > 正文

Java Double相加出现的怪事

2016年12月03日 ⁄ 综合 ⁄ 共 4253字 ⁄ 字号 评论关闭

问题的提出:   

  编译运行下面这个程序会看到什么

[java] view
plain
copy

  1. public class test {  
  2.     public static void main(String args[]) {  
  3.         System.out.println(0.05 + 0.01);  
  4.         System.out.println(1.0 - 0.42);  
  5.         System.out.println(4.015 * 100);  
  6.         System.out.println(123.3 / 100);  
  7.     }  
  8. };  


你没有看错!结果确实是

[java] view
plain
copy

  1. 0.060000000000000005     
  2. 0.5800000000000001     
  3. 401.49999999999994     
  4. 1.2329999999999999     


Java中的简单浮点数类型float和double不能够进行运算。不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,但是多试几次(可以做一个循环)就可以试出类似上面的错误。现在终于理解为什么要有BCD码了。   
  这个问题相当严重,如果你有9.999999999999元,你的计算机是不会认为你可以购买10元的商品的。   
  在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。现在让我们看看如何解决这个问题。    
    解决方案   
  现在我们已经可以解决这个问题了,原则是使用BigDecimal并且一定要用String来够造。   
  但是想像一下吧,如果我们要做一个加法运算,需要先将两个浮点数转为String,然后够造成BigDecimal,在其中一个上调用add方法,传入另一个作为参数,然后把运算的结果(BigDecimal)再转换为浮点数。你能够忍受这么烦琐的过程吗?下面我们提供一个工具类Arith来简化操作。它提供以下静态方法,包括加减乘除和四舍五入:

[java] view
plain
copy

  1. public static double add(double v1, double v2);  
  2.   
  3.     public static double sub(double v1, double v2);  
  4.   
  5.     public static double mul(double v1, double v2);  
  6.   
  7.     public static double div(double v1, double v2);  
  8.   
  9.     public static double div(double v1, double v2, int scale);  
  10.   
  11.     public static double round(double v, int scale);  


[java] view
plain
copy

  1. package org.nutz.mvc.core;  
  2.   
  3. import java.math.BigDecimal;  
  4.   
  5. public class Arith {  
  6.     // 源文件Arith.java:  
  7.   
  8.     /** 
  9.      * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 确的浮点数运算,包括加减乘除和四舍五入。 
  10.      */  
  11.   
  12.     // 默认除法运算精度  
  13.     private static final int DEF_DIV_SCALE = 10;  
  14.   
  15.     // 这个类不能实例化  
  16.     private Arith() {  
  17.     }  
  18.   
  19.     /** 
  20.      * 提供精确的加法运算。 
  21.      *  
  22.      * @param v1 
  23.      *            被加数 
  24.      * @param v2 
  25.      *            加数 
  26.      * @return 两个参数的和 
  27.      */  
  28.   
  29.     public static double add(double v1, double v2) {  
  30.         BigDecimal b1 = new BigDecimal(Double.toString(v1));  
  31.         BigDecimal b2 = new BigDecimal(Double.toString(v2));  
  32.         return b1.add(b2).doubleValue();  
  33.     }  
  34.   
  35.     /** 
  36.      * 提供精确的减法运算。 
  37.      *  
  38.      * @param v1 
  39.      *            被减数 
  40.      * @param v2 
  41.      *            减数 
  42.      * @return 两个参数的差 
  43.      */  
  44.   
  45.     public static double sub(double v1, double v2) {  
  46.         BigDecimal b1 = new BigDecimal(Double.toString(v1));  
  47.         BigDecimal b2 = new BigDecimal(Double.toString(v2));  
  48.         return b1.subtract(b2).doubleValue();  
  49.     }  
  50.   
  51.     /** 
  52.      * 提供精确的乘法运算。 
  53.      *  
  54.      * @param v1 
  55.      *            被乘数 
  56.      * @param v2 
  57.      *            乘数 
  58.      * @return 两个参数的积 
  59.      */  
  60.   
  61.     public static double mul(double v1, double v2) {  
  62.         BigDecimal b1 = new BigDecimal(Double.toString(v1));  
  63.         BigDecimal b2 = new BigDecimal(Double.toString(v2));  
  64.         return b1.multiply(b2).doubleValue();  
  65.     }  
  66.   
  67.     /** 
  68.      * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。 
  69.      *  
  70.      * @param v1 
  71.      *            被除数 
  72.      * @param v2 
  73.      *            除数 
  74.      * @return 两个参数的商 
  75.      */  
  76.   
  77.     public static double div(double v1, double v2) {  
  78.         return div(v1, v2, DEF_DIV_SCALE);  
  79.     }  
  80.   
  81.     /** 
  82.      * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。 
  83.      *  
  84.      * @param v1 
  85.      *            被除数 
  86.      * @param v2 
  87.      *            除数 
  88.      * @param scale 
  89.      *            表示表示需要精确到小数点以后几位。 
  90.      * @return 两个参数的商 
  91.      */  
  92.   
  93.     public static double div(double v1, double v2, int scale) {  
  94.         if (scale < 0) {  
  95.             throw new IllegalArgumentException(  
  96.                     "The   scale   must   be   a   positive   integer   or   zero");  
  97.         }  
  98.         BigDecimal b1 = new BigDecimal(Double.toString(v1));  
  99.         BigDecimal b2 = new BigDecimal(Double.toString(v2));  
  100.         return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();  
  101.     }  
  102.   
  103.     /** 
  104.      * 提供精确的小数位四舍五入处理。 
  105.      *  
  106.      * @param v 
  107.      *            需要四舍五入的数字 
  108.      * @param scale 
  109.      *            小数点后保留几位 
  110.      * @return 四舍五入后的结果 
  111.      */  
  112.   
  113.     public static double round(double v, int scale) {  
  114.         if (scale < 0) {  
  115.             throw new IllegalArgumentException(  
  116.                     "The   scale   must   be   a   positive   integer   or   zero");  
  117.         }  
  118.         BigDecimal b = new BigDecimal(Double.toString(v));  
  119.         BigDecimal one = new BigDecimal("1");  
  120.         return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();  
  121.     }  
  122. }

抱歉!评论已关闭.