Java BigDecimal 8种舍入模式 源码分析&图解

"对比 java.util.Math类 四舍五入方法"

Posted by Gordon on February 21, 2018

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()虽然可以实现数字四舍五入的功能,但其形参是doublefloat,本身传值就可能发生精度丢失,所以不适用于高精度的大数据运算

而且要改为支持指定精确位数,需要对传统Math.round()再多加一次运算:

Math.round(1.02456789 * 100) * 0.01d;//精确到两位小数

如果在银行、金融等行业需要对数值进行高精度计算时,该方法明显不行,所以需要BigDecimal类提供更加丰富的舍入功能。

BigDecimal 8种舍入模式

官方JDK9 BigDecimal RoundingMode的API文档给出了8种舍入模式,但这几种舍入模式相识度略高,所以笔者对源码稍做分析并辅以图解来帮助记忆。

UP

向远离零的方向舍入。舍弃非零部分,并将非零舍弃部分相邻的一位数字加一。

Rounding mode UP Examples

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

向接近零的方向舍入。舍弃非零部分,同时不会非零舍弃部分相邻的一位数字加一,采取截取行为。

Rounding mode DOWN Examples

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一致。
  • 注意:此模式不会减少数值大小。

Rounding mode CEILING Examples

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一致。
  • 注意:此模式不会增加数值大小。

Rounding mode FLOOR Examples

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相同。
  • 这种模式也就是我们常说的我们的“四舍五入”。

Rounding mode HALF-UP Examples

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相同。
  • 这种模式也就是我们常说的我们的“五舍六入”。

Rounding mode HALF-DOWN Examples

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 相同。
  • 注意:在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。
  • 此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况,如果前一位为奇数,则入位,否则舍去。

Rounding mode HALF-EVEN Examples

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。

Rounding mode UNNECESSARY Examples

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