android_基础_BigDecimal 更精准的计算

121 篇文章 1 订阅

转载自: https://blog.csdn.net/JM_beizi/article/details/51775849?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link

问题:为什么要用BigDecimal?

在java 中
1.11+0.9 = 1.2 ? NO result=1.2000000000000002
1.2/3=0.4? NO result=0.39999999999999997

这就是为什么要用BigDecimal的原因,对,因为直接用浮点数进行运算是不准确的,这和计算机对浮点数的存储有关系。

BigDecimal使用过程中的注意事项

这里主要是 BigDecimal 工具类的一个封装,对于其原理不做深究。使用中我觉得BigDecimal(double val) 是个需要注意的点。
其实java 的API 已经介绍的很详细了。首先看一下BigDecimal(double val)的API 介绍。

public BigDecimal(double val)

将 double 转换为 BigDecimal,后者是 double 的二进制浮点值准确的十进制表示形式。返回的 BigDecimal 的标度是使 (10scale × val) 为整数的最小值。

注:

  1. 此构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。

  2. 另一方面,String 构造方法是完全可预知的:写入 new BigDecimal(“0.1”) 将创建一个 BigDecimal,它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法。

  3. 当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。

参数:
val - 要转换为 BigDecimal 的 double 值。
抛出:
NumberFormatException - 如果 val 为无穷大或 NaN。

我的理解:

  1. 通过官文的注释的第1,2点,我们应该能知道大概是什么情况。因为 new BigDecimal(double val)有不可预知性,所以直接用new BigDecimal(double val) 进行表示或者运算同样不能达到我们预期的结果。但是官方文档建议我们优先用可预知的new BigDecimal(String val)构造方法。

  2. 关于注释中的第三点。我觉得翻译的有偏差。我是这样理解的:当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换。BigDecimal (double val)不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String,而要获取这种分步骤转换的结果,建议使用 static valueOf(double) 方法。而这个方法是java api中提供的。

static valueOf(double) API介绍和源码

既然这里官方推荐使用 static valueOf(double) 方法,我们看下static valueOf(double)这个方法的api介绍和源码。

public static BigDecimal valueOf(double val)

使用 Double.toString(double) 方法提供的 double 规范的字符串表示形式将 double 转换为 BigDecimal。
注:这通常是将 double(或 float)转化为 BigDecimal 的首选方法,因为返回的值等于从构造 BigDecimal(使用 Double.toString(double) 得到的结果)得到的值。

参数:
val - 要转换为 BigDecimal 的 double。
返回:
其值等于或约等于 val 值的 BigDecimal。
抛出:
NumberFormatException - 如果 val 为无穷大或 NaN。

源码:

    /**
     * Returns a new {@code BigDecimal} instance whose value is equal to {@code
     * val}. The new decimal is constructed as if the {@code BigDecimal(String)}
     * constructor is called with an argument which is equal to {@code
     * Double.toString(val)}. For example, {@code valueOf("0.1")} is converted to
     * (unscaled=1, scale=1), although the double {@code 0.1} cannot be
     * represented exactly as a double value. In contrast to that, a new {@code
     * BigDecimal(0.1)} instance has the value {@code
     * 0.1000000000000000055511151231257827021181583404541015625} with an
     * unscaled value {@code 1000000000000000055511151231257827021181583404541015625}
     * and the scale {@code 55}.
     *
     * @param val
     *            double value to be converted to a {@code BigDecimal}.
     * @return {@code BigDecimal} instance with the value {@code val}.
     * @throws NumberFormatException
     *             if {@code val} is infinite or {@code val} is not a number
     */
    public static BigDecimal valueOf(double val) {
        if (Double.isInfinite(val) || Double.isNaN(val)) {
            throw new NumberFormatException("Infinity or NaN: " + val);
        }
        return new BigDecimal(Double.toString(val));
    }

这样就很清楚了。 因此看到网上有 new BigDecimal(Double.toString(double val)) 这种写法的 何不直接替换成 valueOf(double val)呢?

BigDecimal工具类

目前有的功能包括,“字符串转double类型”,“double四舍五入”,“BigDecimal四舍五入”,“double保留两位小数”,“BigDecimal保留两位小数”,“double的加、减、乘、除运算”,“BigDecimal 的加、减、乘、除运算”。

以后遇到需要的功能还会慢慢添加

    /**
     * Created by skx
     * <p/>
     * Double 数据类型处理工具类。目前基本包括“字符串转double 类型”,“四舍五入”,“保留两位小数”,“double类型的加、减、乘、除运算”
     */
    public class DoubleFormatTool {
    
        /**
         * 字符串转Double
         *
         * @param doubleString double类型的字符串
         * @return
         */
        public static double strToDouble(String doubleString) {
            try {
                return Double.parseDouble(doubleString);
            } catch (Exception e) {
                return 0.0;
            }
        }
    
        /**
         * 得到一个Double 类型的数 四舍五入后保留小数点后两位.
         * <p/>
         * 这里调用的是  getRoundHalfUpDouble(String doubleString) 方法,
         * 而不是用  BigDecimal b = new BigDecimal(d);return b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); 这样的方式来。
         * 主要原因是 BigDecimal(double) 的构造参数在使用的时候会存在精度不准确的情况,而在java中浮点运算本身就是不精确的,是用IEEE标准来表示的,当然这并不是所有的浮点数都不正确,而是有一部分不准确。
         * <p/>
         * API 中是这样解释的。public BigDecimal(double val)
         * 将 double 转换为 BigDecimal,后者是 double 的二进制浮点值准确的十进制表示形式。返回的 BigDecimal 的标度是使 (10scale × val) 为整数的最小值。
         * <p/>
         * 注:
         * 1.此构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。
         * 2.另一方面,String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法。3.
         * 当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。
         * <p/>
         * eg:  1.115  用BigDecimal(double) 的构造方法四舍五入,保留2位小数得到的值位 1.11  而BigDecimal(String) 的构造方法四舍五入,保留2位小数得到的值位 1.12
         * 而且官方的API 也是推荐使用BigDecimal(String)的构造函数。
         *
         * @param d double类型的字符串
         * @return 四舍五入后保留2位小数的值
         */
        public static double getRoundHalfUpDouble(double d) {
            return getRoundHalfUpDouble(String.valueOf(d));
        }
    
        /**
         * 对于一个double 型的字符串进行四舍五入和保留两位小数处理,如果最后一位小数位为0,则默认不显示
         *
         * @param doubleString double类型的字符串
         * @return 四舍五入后保留2位小数的值
         */
        public static double getRoundHalfUpDouble(String doubleString) {
            try {
                BigDecimal b = new BigDecimal(doubleString);
                return b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            } catch (Exception e) {
                return 0.0;
            }
        }
    
        /**
         * 对于一个double 型的字符串进行四舍五入和保留两位小数处理,如果最后一位小数位为为0,则仍然显示。
         *
         * @param doubleString double类型的字符串
         * @return
         */
        public static String getRoundHalfUpDoubleStr(String doubleString) {
            try {
                double tempDoubleString = getRoundHalfUpDouble(doubleString);
                return String.format("%.2f", tempDoubleString);
            } catch (Exception e) {
                return doubleString;
            }
        }
    
        /**
         * 精确的double类型的字符串  加法运算
         *
         * @param doubleStr1 被加数
         * @param doubleStr2 加数
         * @return 两个参数的和
         */
        public static double add(String doubleStr1, String doubleStr2) {
            if (TextUtils.isEmpty(doubleStr1) || TextUtils.isEmpty(doubleStr2)) {
                return 0;
            }
            try {
                BigDecimal b1 = new BigDecimal(doubleStr1);
                BigDecimal b2 = new BigDecimal(doubleStr2);
                return b1.add(b2).doubleValue();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return 0;
        }
    
        /**
         * 对double 类型的浮点数进行加法运算
         *
         * @param v1 被加数
         * @param v2 加数
         * @return 两个参数的和
         */
        public static double add(double v1, double v2) {
    
            BigDecimal b1 = BigDecimal.valueOf(v1);
            BigDecimal b2 = BigDecimal.valueOf(v2);
            return b1.add(b2).doubleValue();
        }
    
        /**
         * 对double 类型的浮点数进行减法运算。
         *
         * @param v1 被减数
         * @param v2 减数
         * @return 两个参数的差
         */
        public static double subtract(double v1, double v2) {
            BigDecimal b1 = BigDecimal.valueOf(v1);
            BigDecimal b2 = BigDecimal.valueOf(v2);
            return b1.subtract(b2).doubleValue();
        }
    
        /**
         * 对double 类型的浮点数进行乘法运算。
         *
         * @param v1 被乘数
         * @param v2 乘数
         * @return 两个参数的积
         */
        public static double multiply(double v1, double v2) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            return b1.multiply(b2).doubleValue();
        }
    
        /**
         * 对double 类型的浮点数进行除法运算。当发生除不尽的情况时,由scale参数指
         * 定精度,以后的数字四舍五入。
         *
         * @param v1    被除数
         * @param v2    除数
         * @param scale 表示表示需要精确到小数点以后几位。
         * @return 两个参数的商
         */
        public static double divide(double v1, double v2, int scale) {
            if (scale < 0) {
                throw new IllegalArgumentException("The scale must be a positive integer or zero");
            }
            BigDecimal b1 = BigDecimal.valueOf(v1);
            BigDecimal b2 = BigDecimal.valueOf(v2);
            return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
        }
    }

BigDecial devide方法详解

1、首先说一下用法,BigDecimal中的divide主要就是用来做除法的运算。其中有这么一个方法.

public BigDecimal divide(BigDecimal divisor,int scale, int roundingMode)

第一个参数是除数,第二个参数代表保留几位小数,第三个代表的是使用的模式。

    BigDecimal.ROUND_DOWN:直接省略多余的小数,比如1.28如果保留1位小数,得到的就是1.2
    
    BigDecimal.ROUND_UP:直接进位,比如1.21如果保留1位小数,得到的就是1.3
    
    BigDecimal.ROUND_HALF_UP:四舍五入,2.35保留1位,变成2.4
    
    BigDecimal.ROUND_HALF_DOWN:四舍五入,2.35保留1位,变成2.3
    
    后边两种的区别就是如果保留的位数的后一位如果正好是5的时候,一个舍弃掉,一个进位。

2、BigDecimal.setScale()方法用于格式化小数点

setScale(1)表示保留一位小数,默认用四舍五入方式 
setScale(1,BigDecimal.ROUND_DOWN)直接删除多余的小数位,如2.35会变成2.3 
setScale(1,BigDecimal.ROUND_UP)进位处理,2.35变成2.4 
setScale(1,BigDecimal.ROUND_HALF_UP)四舍五入,2.35变成2.4
setScaler(1,BigDecimal.ROUND_HALF_DOWN)四舍五入,2.35变成2.3,如果是5则向下舍
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java BigDecimalJava中用于高精度计算的类。它允许我们进行任意精度的数字计算,而不会出现舍入误差。 在Java中,基本数据类型(如int、double等)的计算是有限制的。例如,double类型只能存储15位有效数字,而且在计算过程中可能会出现舍入误差。这在需要精确计算的场合下是不可接受的,这就需要使用BigDecimal类。 以下是一些Java BigDecimal的常用方法: 1. 实例化BigDecimal对象 可以使用BigDecimal的构造函数来实例化一个对象,例如: ``` BigDecimal num1 = new BigDecimal("1234.5678"); BigDecimal num2 = new BigDecimal(9876.5432); ``` 2. 加法、减法、乘法和除法 可以使用add()、subtract()、multiply()和divide()方法进行加、减、乘和除运算,例如: ``` BigDecimal result1 = num1.add(num2); BigDecimal result2 = num1.subtract(num2); BigDecimal result3 = num1.multiply(num2); BigDecimal result4 = num1.divide(num2, 2, RoundingMode.HALF_UP); // 保留两位小数 ``` 3. 取反、取绝对值、取反余弦等 可以使用negate()、abs()、acos()等方法进行相应的计算,例如: ``` BigDecimal result5 = num1.negate(); // 取反 BigDecimal result6 = num1.abs(); // 取绝对值 BigDecimal result7 = new BigDecimal(Math.PI).acos(); // 取反余弦 ``` 4. 比较大小 可以使用compareTo()方法进行大小比较,例如: ``` int cmp = num1.compareTo(num2); if (cmp > 0) { System.out.println("num1 > num2"); } else if (cmp < 0) { System.out.println("num1 < num2"); } else { System.out.println("num1 = num2"); } ``` 以上是Java BigDecimal的一些常用方法,使用BigDecimal类可以很方便地进行高精度计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值