高精度计算BigDecimal,DecimalFormat

编码中通常遇到金额以及小数点精度计算,为避免精度丢失,将double类型转换为BigDecimal再进行相关计算,或者使用DecimalFormat进行转换。

1.BigDecimal

BigDecimal是Java提供的一个不变的、任意精度的有符号十进制数对象,继承自Number类。BigDecimal类提供了算术操作,操作规模,四舍五入,比较,哈希和格式转换。

BigDecimal常用的构造方法及普通方法:
  • 将double表示形式转换为BigDecimal
   public BigDecimal(double val)
  • 将int表示形式转换为BigDecimal
   public BigDecimal(int val)
  • 将字符串表示形式转换为BigDecimal
   public BigDecimal(String val)
  • 加法
   public BigDecimal add(BigDecimal augend)
  • 减法
   public BigDecimal subtract(BigDecimal subtrahend)
  • 乘法
   public BigDecimal multiply(BigDecimal multiplicand)
  • 除法
   public BigDecimal divide(BigDecimal divisor)
  • 取余
   public BigDecimal remainder(BigDecimal divisor)
  • 求商和余数
   public BigDecimal[] divideAndRemainder(BigDecimal divisor)
  • BigDecimal中setScale(int newScale)方法设置保留几位小数以及舍入模式
   public BigDecimal setScale(int newScale) {
       return setScale(newScale, RoundingMode.UNNECESSARY);
   }
   // ......
   public BigDecimal setScale(int newScale, int roundingMode) {
       return setScale(newScale, RoundingMode.valueOf(roundingMode));
   }
BigDecimal常见的计算方法如下:

    /**
     * 加法运算。
     *
     * @param amount1 被加数
     * @param amount2 加数
     * @return 两个参数的和
     */
    public static double add(double amount1, double amount2) {
        BigDecimal mDecimal1 = new BigDecimal(Double.toString(amount1));
        BigDecimal mDecimal2 = new BigDecimal(Double.toString(amount2));
        return mDecimal1.add(mDecimal2).doubleValue();
    }

    /**
     * 减法运算。
     *
     * @param amount1 被减数
     * @param amount2 减数
     * @return 两个参数的差
     */
    public static double subtraction(double amount1, double amount2) {
        BigDecimal mDecimal1 = new BigDecimal(Double.toString(amount1));
        BigDecimal mDecimal2 = new BigDecimal(Double.toString(amount2));
        return mDecimal1.subtract(mDecimal2).doubleValue();
    }

    /**
     * 乘法运算。
     *
     * @param amount1 被乘数
     * @param amount2 乘数
     * @return 两个参数的积
     */
    public static double multiplication(double amount1, double amount2) {
        BigDecimal mDecimal1 = new BigDecimal(Double.toString(amount1));
        BigDecimal mDecimal2 = new BigDecimal(Double.toString(amount2));
        return mDecimal1.multiply(mDecimal2).doubleValue();
    }

    /**
     * 除法运算。
     * MathContext.DECIMAL128其精度设置与Decimal128格式,34位数字和 [HALF_EVEN]的舍入模式一样。
     * divide()方法默认为MathContext.UNLIMITED无限取值,不做限制陷入死循环会抛出ArithmeticException(计算溢出)异常。
     *
     * @param amount1 被除数
     * @param amount2 除数
     * @return 两个参数的商
     */
    public static double divisionOperation(double amount1, double amount2) {
        BigDecimal mDecimal1 = new BigDecimal(Double.toString(amount1));
        BigDecimal mDecimal2 = new BigDecimal(Double.toString(amount2));
        return mDecimal1.divide(mDecimal2, MathContext.DECIMAL128).doubleValue();
    }

2.DecimalFormat

DecimalFormat是一个NumberFormat的子类的格式小数。它有各种各样的功能设计使它可以解析和格式化数字在任何地区,包括支持西方,阿拉伯语和印度语的数字。它还支持不同类型的数据,包括整数(123),定点数(123.4),科学记数法(1.23 e4)百分比(12%)和货币数量(123美元)等。

DecimalFormat常用的构造方法及普通方法:
  • 创建一个DecimalFormat使用默认模式(Locale.Category.FORMAT)
public DecimalFormat() {
  Locale def = Locale.getDefault(Locale.Category.FORMAT);
  // try to get the pattern from the cache
  String pattern = cachedLocaleData.get(def);
  if (pattern == null) {  /* cache miss */
   // Get the pattern for the default locale.
   pattern = LocaleData.get(def).numberPattern;
   /* update cache */
   cachedLocaleData.putIfAbsent(def, pattern);
  }
   this.symbols = new DecimalFormatSymbols(def);
   init(pattern);
}
  • 创建一个使用给定的DecimalFormat模式和默认
    public DecimalFormat(String pattern)
  • 创建一个使用给定的DecimalFormat模式和符号。
    public DecimalFormat(String pattern, DecimalFormatSymbols symbols)
  • 适用于给定的地区设置固定的格式 ,如”#0.00”—>1.25
    public void applyPattern(String pattern)
  • 格式化生成一个字符串
    public final String format(double number)
  • 设置格式时这个数字格式所使用的货币币值。
    public void setCurrency(Currency currency)
  • 设置数的小数部分所允许的最大位数
    public void setMaximumFractionDigits(int newValue)
  • 设置数的小数部分所允许的最小位数
    public void setMinimumFractionDigits(int newValue)
  • 设置区域
    public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols)
  • 设置返回前缀
   public void setNegativePrefix(String newValue)
  • 设置前后缀
   public void setPositiveSuffix(String newValue)
  • 设置分组
    public void setGroupingUsed(boolean newValue)
  • 设置分组大小
    public void setGroupingSize(int newValue)
DecimalFormat常用方法如下:
    /**
     * 格式化保留两位小数,默认四舍五入
     *
     * @param amount
     * @return
     */
    public static String formatTwoDecimals(double amount) {
        DecimalFormat mFormat = new DecimalFormat();
        // 设置区域
        mFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.CHINA));
        // 设置格式化保留两位小数
        mFormat.applyPattern("#0.00");
        return mFormat.format(amount);
    }

    /**
     * 格式化保留两位小数,只舍不入
     *
     * @param amount
     * @return
     */
    public static String formatTwoDecimalsDown(double amount) {
        DecimalFormat mFormat = new DecimalFormat();
        // 设置区域
        mFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.CHINA));
        // 设置格式化保留两位小数
        mFormat.applyPattern("#0.00");
        // 设置只舍不入
        mFormat.setRoundingMode(RoundingMode.DOWN);
        return mFormat.format(amount);
    }

    /**
     * 保留有效数,最多保留两位
     *
     * @return
     */
    public static String formatTwoEffectiveNumber(double amount) {
        DecimalFormat mFormat = new DecimalFormat();
        // 设置区域
        mFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.CHINA));
        // 设置保留两位有效数
        mFormat.applyPattern("#0.##");
        // 设置只舍不入
        mFormat.setRoundingMode(RoundingMode.DOWN);
        return mFormat.format(amount);
    }

    /**
     * 金额3位数分组,如10000——>10,000
     *
     * @param amount
     * @return
     */
    public static String formatAmountGrouping(double amount) {
        DecimalFormat mFormat = new DecimalFormat();
        // 设置区域
        mFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.CHINA));
        // 设置使用逗号“,”将金额分隔开
        mFormat.setGroupingUsed(true);
        // 设置分隔位数
        mFormat.setGroupingSize(3);
        return mFormat.format(amount);
    }
DecimalFormat中格式字符说明
符号 位置 本地化 含义
0 数字 阿拉伯数字
# 数字 阿拉伯数字,如果不存在则显示为空
. 数字 小数分隔符或货币小数分隔符
- 数字 缺省负数前缀
, 数字 分组分隔符
E 数字 分隔科学计数法中的尾数和指数。在前缀或后缀中无需加引号
% 前缀或后缀 乘以100和作为百分比显示
/u2030 前缀或后缀 乘以1000并显示为千分数
3.BigDecimal和DecimalFormat中保留小数位进行舍入模式都是RoundingMode模式

源码如下:

public enum RoundingMode {

    /**
     * 向上进位,如:
     * 2.56计算后保留一位小数则为2.6
     * 1.1保留整数则为2
     * -2.5保留整数为-3
     */
    UP(BigDecimal.ROUND_UP),

    /**
     * 舍入模式为零。
     */
    DOWN(BigDecimal.ROUND_DOWN),

    /**
     * 舍入模式正无穷。 
     */
    CEILING(BigDecimal.ROUND_CEILING),

    /**
     * 舍入模式向负无穷。  
     */
    FLOOR(BigDecimal.ROUND_FLOOR),

    /**
     * 舍入模式向“最近邻”轮除非邻居都是等距的,在这种情况下一轮。 
     */
    HALF_UP(BigDecimal.ROUND_HALF_UP),

    /**
     * 舍入模式向“最近邻”轮除非邻居都是等距的,在这种情况下一轮下来。 
     */
    HALF_DOWN(BigDecimal.ROUND_HALF_DOWN),

    /**
     * 舍入模式向“最近邻”轮除非邻居都是等距的,在这种情况下,甚至向邻居。 
     */
    HALF_EVEN(BigDecimal.ROUND_HALF_EVEN),

    /**
     * 舍入模式断言请求的操作有一个确切的结果,因此不需要舍入。
     */
    UNNECESSARY(BigDecimal.ROUND_UNNECESSARY);

    private final int bigDecimalRM;

    RoundingMode(int rm) {
        bigDecimalRM = rm;
    }

    /**
     * 返回与指定名称这种类型的枚举常量。
     * 字符串必须匹配完全一个标识符用于声明一个枚举常数在这个类型。 (多余的空格字符是不允许的。) 参数 
     */
    public static RoundingMode valueOf(int mode) {
        switch (mode) {
            case BigDecimal.ROUND_CEILING:
                return CEILING;
            case BigDecimal.ROUND_DOWN:
                return DOWN;
            case BigDecimal.ROUND_FLOOR:
                return FLOOR;
            case BigDecimal.ROUND_HALF_DOWN:
                return HALF_DOWN;
            case BigDecimal.ROUND_HALF_EVEN:
                return HALF_EVEN;
            case BigDecimal.ROUND_HALF_UP:
                return HALF_UP;
            case BigDecimal.ROUND_UNNECESSARY:
                return UNNECESSARY;
            case BigDecimal.ROUND_UP:
                return UP;
            default:
                throw new IllegalArgumentException("Invalid rounding mode");
        }
    }
}

你可能感兴趣的:(Android,Java,Android,BigDecimal)