我在做一个算法时,发现java中出现了一个奇怪的误差,数值均以double型处理
1/3.0=0.33333333333333333333(16个)
2/3.0=0.66666666666666666666(16个)
这是正确的
可是~~~~~
8/3.0=2.66666666666666666665      这是为什么呢?
64/3.0=21.3333333333333333332     这又是为什么呢?
原先我以为是假分数造成的,可是~~~~~~~~
32/3.0=10.6666666666666666666     正确这下好,有没有人能够解释一下,这是为什么呢?我想应该和内部机制有关,请教高人给个解释

解决方案 »

  1.   


    System.out.println(new BigDecimal(1 / 3.0));
                System.out.println(new BigDecimal(64 / 3.0));看看这个输出结果研究吧
      

  2.   

    这个问题已经讨论过了
    要自己写程序解决float 的精度问题
         项目按说都结项了, 但最近出了不大不小的麻烦, 就是因为程序中对金额税率的计算使用了float型的数据,造成结果精度不够,所以数据有损失。 多张报表的结果核对不上。   
         上网查了一些相关的资料,发现这是因为float的精度只有7位(小数点后7位)。 比如在保存1.05(日本的现行税率)这个数时,float型的变量实际存储的是1.04999998...  (由于1.05在用2进制保存时,和我们看到的10进制的表示不同,它的2进制表示是小数点后无穷尽的,学过《计算机组成原理》的同学都知道) 。 正是因为这样,在和一个较大的数(上百万,在日本的这个数额稀松平常)的相乘以后,误差就被放大了。
          然后继续查了一些解决方法,double型的数据精度比float要高,但也就只有11位,也就是说,能对付上百亿。 对于我们目前的系统来说,应该是够用了,日常单比业务不会超过千万,月季的时候,也不会到10亿。过亿的数据也都是整形基础上合计的。
          对于精度要求更高的程序,Java 提供一个BigDecimal 类,
          但要注意,该类不是一个基本类型,而是一个封装的类。 所以在使用中非常不便的是,它的运算都是通过函数来完成,而不是通过+-*/ 之类的运算符来完成。
          所以,这里告诫大家, 在做财务相关的软件时,一定要慎重选择小数的类型或类。 
      

  3.   

    给你个Demo吧解决方案现在我们已经可以解决这个问题了,原则是使用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: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();}};
      

  4.   

    原则是使用BigDecimal并且一定要用String来够造。
      

  5.   

    这是由于double类型在计算机中存储的机制造成的
      

  6.   

    5楼的高人,问题真的解决了吗?我使用你给出的程序代码仍然达不到我想要的效果,当我把
    private static final int DEF_DIV_SCALE = 10;
    改为
    private static final int DEF_DIV_SCALE = 16;
    的时候得到的结果依然是 2.6666666666666665看来内部的机制果然是铁的定律,无法抗拒啊??~~!!
    我已经有些疑惑了,是不是只要开始规定了double的数字之后,当2.6666666666666666出现,程序就会认为是2.6666666666666665
    至少我在使用了BigDecimal之后,认为是这样因为我设定
    double ft = 2.6666666666666666;
    double et = 8.0/3;
    BigDecimal fd = new BigDecimal(ft);
    BigDecimal ed = new BigDecimal(et);
    System.out.println(fd);
    System.out.println(ed);
    显示出来的结果是一样的
    2.666666666666666518636930049979127943515777587890625
    而且
    System.out.println(ft);
    System.out.println(et);
    显示出来的结果是一样的
    2.6666666666666665
    非常感谢你们的回答,不过真理似乎还离我们有一定的距离啊
      

  7.   

    答:不是啊。使用BigDecimal可以达到任意精度啊。楼主的代码不大对啊,才产生你的问题啊:
    double ft = 2.6666666666666666; 
    double et = 8.0/3; 
    BigDecimal fd = new BigDecimal(ft); //不要这样。改为:BigDecimal fd = new BigDecimal("2.666666666666666666"); 
    BigDecimal ed = new BigDecimal(et);//不要这样,改为: BigDecimal ed =  new BigDecimal("8.0").divide(new BigDecimal("3"),MathContext.DECIMAL128); 
           System.out.println("fd="+fd); 
    System.out.println("ed="+ed); 
    结果将是:
    fd=2.666666666666666666
    ed=2.666666666666666666666666666666667