前言
项目中明确规定,凡是涉及到金额的属性定义都必须使用BigDecimal类型,这里来和大家一起学习下BigDecimal的正确打开方式。
先来个错误的示范
话不多说,直接上demo
@Test
public void wrongUseDecimal() {
BigDecimal test1 = new BigDecimal(1.745);
BigDecimal test2 = new BigDecimal(0.745);
test1 = test1.setScale(2, BigDecimal.ROUND_HALF_UP);
test2 = test2.setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println(test1);
System.out.println(test2);
}
上面是对1.745和0.745四舍五入保留两位有效数字,输出结果如下
1.75
0.74
使用参数为float或double的BigDecimal创建对象会丢失精度。
System.out.println(new BigDecimal(1.745));
// 1.74500000000000010658141036401502788066864013671875
System.out.println(new BigDecimal(0.745));
// 0.74499999999999999555910790149937383830547332763671875
如上面所示,因为精度丢失,所以在四舍五入的时候没有达到预期目标。大家不要觉得不就相差0.01么,影响不大吧,但是如果这两个数字后面的单位是亿呢?因此强烈建议不要使用参数为float或double的BigDecimal创建对象。
正确使用姿势
//使用BigDecimal(String val)的构造方法创建对象
new BigDecimal("1.745");
new BigDecimal("0.745");
//使用BigDecimal的valueOf(double val)方法创建对象
BigDecimal.valueOf(1.745);
BigDecimal.valueOf(0.745);
其实BigDecimal.valueOf也是将浮点类型转换成字符串类型之后再创建BigDecimal对象的
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}
拓展DecimalFormat
DecimalFormat
是 NumberFormat
的一个具体子类,用于格式化十进制数字。该类设计有各种功能,使其能够解析和格式化任意语言环境中的数,包括对西方语言、阿拉伯语和印度语数字的支持。它还支持不同类型的数,包括整数 (123)、定点数 (123.4)、科学记数法表示的数 (1.23E4)、百分数 (12%) 和金额 ($123)。所有这些内容都可以本地化。
符号 | 位置 | 本地化? | 含义 |
---|---|---|---|
0 | 数字 | 是 | 阿拉伯数字 |
# | 数字 | 是 | 阿拉伯数字如果不存在就显示为空 |
. | 数字 | 是 | 小数分隔符或货币小数分隔符 |
- | 数字 | 是 | 减号 |
, | 数字 | 是 | 分组分隔符 |
E | 数字 | 是 | 分割科学技术法中的尾数和指数。在前缀和后缀中无需添加引号 |
; | 子模式边界 | 是 | 分隔正数和负数子模式 |
% | 前缀或后缀 | 是 | 乘以100并显示为百分数 |
/u2030 | 前缀或后缀 | 是 | 乘以1000并显示为千分数 |
¤ (\u00A4) | 前缀或后缀 | 否 | 货币记号,由货币符号替换。如果两个同时出现,则用国际货币符号替换。如果出现在某个模式中,则使用货币小数分隔符,而不使用小数分隔符 |
' | 前缀或后缀 | 否 | 用于在前缀或或后缀中为特殊字符加引号,例如 "'#'#" 将 123 格式化为 "#123"。要创建单引号本身,请连续使用两个单引号:"# o''clock" |
测试Demo如下
double pi = 3.1415927;//圆周率
//取一位整数
System.out.println(new DecimalFormat("0").format(pi));//3
//取一位整数和两位小数
System.out.println(new DecimalFormat("0.00").format(pi));//3.14
//取两位整数和三位小数,整数不足部分以0填补。
System.out.println(new DecimalFormat("00.000").format(pi));// 03.142
//取所有整数部分
System.out.println(new DecimalFormat("#").format(pi));//3
//以百分比方式计数,并取两位小数
System.out.println(new DecimalFormat("#.##%").format(pi));//314.16%
BigDecimal的等值比较
BigDecimal大小,想必大家不会用>和<来进行大小比较吧,看看下面的demo
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
System.out.println(bd1.equals(bd2)); //false
System.out.println(bd1.compareTo(bd2)); //0
上面的运行结果,其实归结于BigDecimal中的equals方法的实现会比较两个数字的精度,而compareTo方法则只会比较数值的大小。这些小知识日常开发中要多注意,写代码最怕太自信,如果经验不足,还是自己多测测。