美文网首页
Android中数值的精确计算

Android中数值的精确计算

作者: luweicheng24 | 来源:发表于2017-09-02 13:24 被阅读0次

Android中数值计算的精度

在平常的开发中,会经常进行数值的计算,而如何才能更加准确的得到计算结果是最重要的,最近在做一个金融类的项目,其中涉及到黄金的计算,所以写这篇文章来总结一下,如何对数值的计算更加精确完美,这是本人在简书的第一篇文章,欢迎大家留言讨论。

  • 问题起源:
    作为一名利用Java的开发人员,当遇到数值计算的时候首先想到的自然就是Double和Float,Double的精度值是16位,Float的精度值为8位,对于精度要求不是足够高的话完全是够用了。本人开始就是采用Double类型的对黄金的克重以及总额来进行计算的,但是在测试中发现了问题,数值小的时候还正确,但是当数值越大差值也就明显了。
    for example:
   double a = 11540.0;
    double b = 0.855;
    Double double1 =  a* b;
    System.out.println("double=="+double1);
   打印结果:double==9866.699999999999

其实这两个数字的乘积是整数:9866.7,其实在Java中为什么会总是出现double值的乘积总是在一个正确的结果左右偏0.0000**1,这是因为当两个double数值相乘时,底层采用转换成二进制来进行乘法的运算,由于在乘完之后在包含小数的二进制中无法完全转换成十进制才会发生这种情况。

  • 解决办法
    Java中有一个类是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.

但是在测试中直接将Double值传进去,也就是调用第一个构造方法来创建的BigDecimal也会损失精度,具体这里先不说了,(完了再看一下源码),因此本人只是利用第二个构造函数来进行对BigDecimal来创建对象的,下面是我写的一个工具类:


/**
 * 
 * 作者: 卢卫成 时间: 2017年9月2日 功能描述: 用于对数值的精确计算工具类
 *
 */
public class CalcUtils {

    public static final int TYPE_ADD = 0x00; // 加法
    public static final int TYPE_MULTIPLY = 0x01; // 乘法
    public static final int TYPE_DIVIDE = 0x02; // 除法
    public static final int TYPE_SUBTRACT = 0x03; // 减法
   /**
    *  加法
    * @param a 
    * @param b
    * @return
    */
    public static Double add(Double a, Double b) {
        return calc(a, b, -1, TYPE_ADD, null);
    }
    /**
     * 减法
     * @param a
     * @param b
     * @return
     */

    public static Double sub(Double a, Double b) {
        return calc(a, b, -1, TYPE_SUBTRACT, null);
    }
    /**
     * 乘法
     * @param a
     * @param b
     * @return
     */

    public static Double multiply(Double a, Double b) {
        return calc(a, b, -1, TYPE_MULTIPLY, null);
    }
    
    /**
     * 除法
     * @param a
     * @param b
     * @return
     */

    public static Double divide(Double a, Double b) {
        return calc(a, b, -1, TYPE_DIVIDE, null);
    }
 
   /**
    * 乘法
    * @param a
    * @param b
    * @param scale 小数点后保留的位数
    * @param mode 保留的模式
    * @return
    */
    public static Double multiply(Double a, Double b, int scale, RoundingMode mode) {

        return calc(a, b, scale, TYPE_MULTIPLY, mode);
    }
      /**
        * 除法
        * @param a
        * @param b
        * @param scale 小数点后保留的位数
        * @param mode 保留的模式
        * @return
        */
    public static Double divide(Double a, Double b, int scale, RoundingMode mode) {

        return calc(a, b, scale, TYPE_DIVIDE, mode);
    }
     /**
      *  计算
      * @param a
      * @param b
      * @param scale
      * @param type
      * @param mode
      * @return
      */
    private static Double calc(Double a, Double b, int scale, int type, RoundingMode mode) {
        BigDecimal result = null;
        
        BigDecimal bgA = new BigDecimal(String.valueOf(a));
        BigDecimal bgB = new BigDecimal(String.valueOf(b));
        switch (type) {
        case TYPE_ADD:
            result = bgA.add(bgB);
            break;
        case TYPE_MULTIPLY:
            result = bgA.multiply(bgB);
            break;
        case TYPE_DIVIDE:
            try {
                result = bgA.divide(bgB);       
            } catch (ArithmeticException e) {// 防止无限循环而报错  采用四舍五入保留3位有效数字
                result = bgA.divide(bgB,3,RoundingMode.HALF_DOWN);  
            }
        
            break;
        case TYPE_SUBTRACT:
            result = bgA.subtract(bgB);
            break;

        }
        if (mode==null) {
            if(scale!=-1){
              
                result = result.setScale(scale);    
            }
        }else{
            if(scale!=-1){
                result = result.setScale(scale,mode);
            }
        }
        return result.doubleValue();
    }

}

这里涉及到两个参数:Scale和RoundingMode

Scale

scale是用来对利用BigDecimal对数值进行运算后保留的位数。

RouningMode

该参数是BigDecimal是一个枚举类,包含有8个枚举类型,用来说明对经过计算后数值的取舍模式:

  • ROUND_UP:远离零方向舍入。向绝对值最大的方向舍入,只要舍弃位非0即进位。
  • ROUND_DOWN:趋向零方向舍入。向绝对值最小的方向输入,所有的位都要舍弃,不存在进位情况。
  • ROUND_CEILING:向正无穷方向舍入。向正最大方向靠拢。若是正数,舍入行为类似于ROUND_UP,若为负数,舍入行为类似于ROUND_DOWN。Math.round()方法就是使用的此模式。
  • ROUND_FLOOR:向负无穷方向舍入。向负无穷方向靠拢。若是正数,舍入行为类似于ROUND_DOWN;若为负数,舍入行为类似于ROUND_UP。
  • HALF_UP:最近数字舍入(5进)。这是我们最经典的四舍五入。
  • HALF_DOWN:最近数字舍入(5舍)。在这里5是要舍弃的。
  • HAIL_EVEN:银行家舍入法。

相关文章

  • Android中数值的精确计算

    Android中数值计算的精度 在平常的开发中,会经常进行数值的计算,而如何才能更加准确的得到计算结果是最重要的,...

  • NSDecimalNumber - 精确的数值计算

    浮点数计算会存在误差float:6-7位有效数double:15-16位有效数decimal:不是基础数据类型,精...

  • mathematica自学整理

    mathematica自学整理 mathematica功能 数值计算,数值精算程序都尽量精确 ,由N()函数控制 ...

  • BigDecimal详解

    float和double设计的目的是为了科学计算和工程计算. 它提供在广域数值范围上较为精确的快速计算. 然而,它...

  • BigDecimal的详细解析

    前言 float和double设计的目的是为了科学计算和工程计算. 它提供在广域数值范围上较为精确的快速计算. 然...

  • BigDecimal运算详细解析

    float和double设计的目的是为了科学计算和工程计算. 它提供在广域数值范围上较为精确的快速计算. 然而,它...

  • Python基础代码的学习

    基础输入输出 精确计算 数值转换与计算 列表基础 列表提高 可变序列及列表通用操作 猜数字游戏

  • 高精度计算(一)

    概述 当计算的数值非常大或是对于计算的精度要求非常高时,用已知的数据类型无法精确地表示数值。可以采用数组来模拟大数...

  • BigDecimal大精度小数

    BigDecimal用于精度计算 输出结果: 在计算数值的时候,由于浮点数没有办法精确的对二进制进行计算,所以经常...

  • TextView 计算文字宽高

    有时候需要在 程序中计算TextView文本的宽高来动态设置一些数值,在Android中可以使用Paint开计算 ...

网友评论

      本文标题:Android中数值的精确计算

      本文链接:https://www.haomeiwen.com/subject/eoaljxtx.html