BigDecimal的引入
首先我们先来看如下代码示例:
public class Test_1 {
public static void main(String[] args) {
System.out.println(0.06+0.01);
System.out.println(1.0-0.42);
System.out.println(4.015*100);
System.out.println(303.1/1000);
}
}
运行结果如下:
0.06999999999999999
0.5800000000000001
401.49999999999994
0.30310000000000004
这结果是不是很神奇
这是因为浮点数无法用二进制进行精确表示,CPU表示浮点数是由指数和尾数组成,这样的结果可能会失去一定的精确度:比如0.7
的二进制表示并非就是精确的0.7
,反而最为接近的二进制表示是 0.6999999999999999
而java.util.Math
类的round()
虽然可以实现数字四舍五入的功能,但其形参是double
和float
,本身传值就可能发生精度丢失,所以不适用于高精度的大数据运算
而且要改为支持指定精确位数,需要对传统Math.round()
再多加一次运算:
Math.round(1.02456789 * 100) * 0.01d;//精确到两位小数
如果在银行、金融等行业需要对数值进行高精度计算时,该方法明显不行,所以需要BigDecimal
类提供更加丰富的舍入功能。
BigDecimal 8种舍入模式
官方JDK9 BigDecimal RoundingMode的API文档给出了8种舍入模式,但这几种舍入模式相识度略高,所以笔者对源码稍做分析并辅以图解来帮助记忆。
UP
向远离零的方向舍入。舍弃非零部分,并将非零舍弃部分相邻的一位数字加一。
Input Number | Input rounded to one digit with UP rounding |
---|---|
5.5 | 6 |
2.5 | 3 |
1.6 | 2 |
1.1 | 2 |
1.0 | 1 |
-1.0 | -1 |
-1.1 | -2 |
-1.6 | -2 |
-2.5 | -3 |
-5.5 | -6 |
DOWN
向接近零的方向舍入。舍弃非零部分,同时不会非零舍弃部分相邻的一位数字加一,采取截取行为。
Input Number | Input rounded to one digit with DOWN rounding |
---|---|
5.5 | 5 |
2.5 | 2 |
1.6 | 1 |
1.1 | 1 |
1 | 1 |
-1 | -1 |
-1.1 | -1 |
-1.6 | -1 |
-2.5 | -2 |
-5.5 | -5 |
CEILING
- 向正无穷的方向舍入。如果为正数,舍入结果同ROUND_UP一致;
- 如果为负数,舍入结果同ROUND_DOWN一致。
- 注意:此模式不会减少数值大小。
Input Number | Input rounded to one digit with CEILING rounding |
---|---|
5.5 | 6 |
2.5 | 3 |
1.6 | 2 |
1.1 | 2 |
1 | 1 |
-1 | -1 |
-1.1 | -1 |
-1.6 | -1 |
-2.5 | -2 |
-5.5 | -5 |
FLOOR
- 向负无穷的方向舍入。如果为正数,舍入结果同ROUND_DOWN一致;
- 如果为负数,舍入结果同ROUND_UP一致。
- 注意:此模式不会增加数值大小。
Input Number | Input rounded to one digit with FLOOR rounding |
---|---|
5.5 | 5 |
2.5 | 2 |
1.6 | 1 |
1.1 | 1 |
1 | 1 |
-1 | -1 |
-1.1 | -2 |
-1.6 | -2 |
-2.5 | -3 |
-5.5 | -6 |
HALF-UP
- 向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。
- 如果舍弃部分>= 0.5,则舍入行为与ROUND_UP相同;否则舍入行为与ROUND_DOWN相同。
- 这种模式也就是我们常说的我们的“四舍五入”。
Input Number | Input rounded to one digit with HALF-DOWN rounding |
---|---|
5.5 | 6 |
2.5 | 3 |
1.6 | 2 |
1.1 | 1 |
1 | 1 |
-1 | -1 |
-1.1 | -1 |
-1.6 | -2 |
-2.5 | -3 |
-5.5 | -6 |
HALF-DOWN
- 向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则为向下舍入的舍入模式。
- 如果舍弃部分> 0.5,则舍入行为与ROUND_UP相同;否则舍入行为与ROUND_DOWN相同。
- 这种模式也就是我们常说的我们的“五舍六入”。
Input Number | Input rounded to one digit with HALF-EVEN rounding |
---|---|
5.5 | 5 |
2.5 | 2 |
1.6 | 2 |
1.1 | 1 |
1 | 1 |
-1 | -1 |
-1.1 | -1 |
-1.6 | -2 |
-2.5 | -2 |
-5.5 | -5 |
HALF-EVEN
- 向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则相邻的偶数舍入。
- 如果舍弃部分左边的数字奇数,则舍入行为与 ROUND_HALF_UP 相同;如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。
- 注意:在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。
- 此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况,如果前一位为奇数,则入位,否则舍去。
Input Number | Input rounded to one digit with HALF-EVEN rounding |
---|---|
5.5 | 6 |
2.5 | 2 |
1.6 | 2 |
1.1 | 1 |
1 | 1 |
-1 | -1 |
-1.1 | -1 |
-1.6 | -2 |
-2.5 | -2 |
-5.5 | -6 |
UNNECESSARY
- 断言请求的操作具有精确的结果,因此不需要舍入。
- 如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。
Input Number | Input rounded to one digit with UNNECESSARY rounding |
---|---|
5.5 | throw ArithmeticException |
2.5 | throw ArithmeticException |
1.6 | throw ArithmeticException |
1.1 | throw ArithmeticException |
1 | 1 |
-1 | -1 |
-1.1 | throw ArithmeticException |
-1.6 | throw ArithmeticException |
-2.5 | throw ArithmeticException |
-5.5 | throw ArithmeticException |