zoukankan      html  css  js  c++  java
  • ________________《转载》以后能用到吧 Float & double的计算问题《转载》

    《转载》
    public class AA {
    public static void main(String[] args) {
    double a=2.0;
    double b=0.0;
    double c=1.9;
    System.out.println(a%b);//输出结果 NaN
    System.out.println(a/b);//输出结果Infinity
    System.out.println(a-c);//输出结果0.10000000000000009
    System.out.println(1.4*1.5);//输出结果2.0999999999999996

    在刚刚做完的一个项目中,遇到了double型计算不精确的问题。到网上查找后,问题得到解决。经验共享,在这里总结一下。

        Java中的浮点数类型float和double不能够进行精确运算。这个问题有时候非常严重。比如,经过double型直接计算,1.4×1.5有时会得出2.0999999999999996的结果,但实际上,应该得到2.10。而且,类似的情况并不仅限于乘法计算。

        在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。所以,在商业计算中我们要用:java.math.BigDecimal 。

        BigDecimal一共有4个构造方法,其中不属于int的有两个,它们是:

        1、BigDecimal(double val)

              Translates a double into a BigDecimal.

        2、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不可。

        一、解决方案:

        现在已经可以解决这个问题了,原则是使用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;
        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();

        }

        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();

        }


        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();

        }


        public static double div(double v1,double v2){

            return div(v1,v2,DEF_DIV_SCALE);

        }


        /**

         * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指

         * 定精度,以后的数字四舍五入。
         */

        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();

        }

        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();

        }

    }

  • 相关阅读:
    sql over(partition by) 开窗函数的使用
    利用curl函数处理GET数据获取微信公众号的access_token
    2018.4.12
    字段和属性
    C#实现回车键登录
    判断DataTable里面数据是否有重复数据
    一个强大的人民币大写转换的正则表达式
    C#将image中的显示的图片转换成二进制
    遍历Dev LayoutControl中的所有控件信息
    遍历窗体中所有控件的信息
  • 原文地址:https://www.cnblogs.com/qiqisx/p/7756290.html
Copyright © 2011-2022 走看看