编码中通常遇到金额以及小数点精度计算,为避免精度丢失,将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");
}
}
}