BigDecimal舍入模式使用及建议
Posted mabaoqing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BigDecimal舍入模式使用及建议相关的知识,希望对你有一定的参考价值。
1. 八种舍入模式
此节内容参考于 https://my.oschina.net/sunchp/blog/670909。
JDK1.5新发布的枚举 RoundingMode 对 BigDecimal 的八种舍入模式进行了封装,用于取代 BigDecimal 中静态常量式的舍入模式,代码如下
public enum RoundingMode { UP(BigDecimal.ROUND_UP), // 0 DOWN(BigDecimal.ROUND_DOWN), // 1 CEILING(BigDecimal.ROUND_CEILING), // 2 FLOOR(BigDecimal.ROUND_FLOOR), // 3 HALF_UP(BigDecimal.ROUND_HALF_UP), // 4 HALF_DOWN(BigDecimal.ROUND_HALF_DOWN), // 5 HALF_EVEN(BigDecimal.ROUND_HALF_EVEN), // 6 UNNECESSARY(BigDecimal.ROUND_UNNECESSARY); // 7 final int oldMode; private RoundingMode(int oldMode) { this.oldMode = oldMode; } public static RoundingMode valueOf(int rm) { switch(rm) { case BigDecimal.ROUND_UP: return UP; case BigDecimal.ROUND_DOWN: return DOWN; case BigDecimal.ROUND_CEILING: return CEILING; case BigDecimal.ROUND_FLOOR: return FLOOR; case BigDecimal.ROUND_HALF_UP: return HALF_UP; case BigDecimal.ROUND_HALF_DOWN: return HALF_DOWN; case BigDecimal.ROUND_HALF_EVEN: return HALF_EVEN; case BigDecimal.ROUND_UNNECESSARY: return UNNECESSARY; default: throw new IllegalArgumentException("argument out of range"); } } }
1. UP
定义:远离零方向舍入。
解释:始终对非零舍弃部分前面的数字加 1。注意,此舍入模式始终不会减少计算值的绝对值。
2. DOWN
定义:向零方向舍入。
解释:从不对舍弃部分前面的数字加 1(即截尾)。注意,此舍入模式始终不会增加计算值的绝对值。
3. CEILING
定义:向正无限大方向舍入。
解释:如果结果为正,则舍入行为类似于 RoundingMode.UP;如果结果为负,则舍入行为类似于 RoundingMode.DOWN。注意,此舍入模式始终不会减少计算值。
4. FLOOR
定义:向负无限大方向舍入。
解释:如果结果为正,则舍入行为类似于 RoundingMode.DOWN;如果结果为负,则舍入行为类似于 RoundingMode.UP。注意,此舍入模式始终不会增加计算值。
5. HALF_UP
定义:向最接近的数字方向舍入,如果与两个相邻数字的距离相等,则向上舍入。
解释:如果被舍弃部分 >= 0.5,则舍入行为同 RoundingMode.UP;否则舍入行为同 RoundingMode.DOWN。注意,此舍入模式就是通常学校里讲的四舍五入。
6. HALF_DOWN
定义:向最接近的数字方向舍入,如果与两个相邻数字的距离相等,则向下舍入。
解释:如果被舍弃部分 > 0.5,则舍入行为同 RoundingMode.UP;否则舍入行为同 RoundingMode.DOWN。注意,此舍入模式就是通常讲的五舍六入。
7. HALF_EVEN
定义:向最接近数字方向舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
解释:如果舍弃部分左边的数字为奇数,则舍入行为同 RoundingMode.HALF_UP;如果为偶数,则舍入行为同 RoundingMode.HALF_DOWN。注意,在重复进行一系列计算时,根据统计学,此舍入模式可以在统计上将累加错误减到最小。此舍入模式也称为“银行家舍入法”,主要在美国使用。此舍入模式类似于 Java 中对 float 和 double 算法使用的舍入策略。
8. UNNECESSARY
定义:用于断言请求的操作具有精确结果,因此不发生舍入。
解释:计算结果是精确的,不需要舍入,否则抛出 ArithmeticException。
官方示例:
根据给定的舍入模式将输入数字舍入为一位数的结果 | ||||||||
输入数字 | UP | DOWN | CEILING | FLOOR | HALF_UP | HALF_DOWN | HALF_EVEN | UNNECESSARY |
5.5 | 6 | 5 | 6 | 5 | 6 | 5 | 6 | throw ArithmeticException |
2.5 | 3 | 2 | 3 | 2 | 3 | 2 | 2 | throw ArithmeticException |
1.6 | 2 | 1 | 2 | 1 | 2 | 2 | 2 | throw ArithmeticException |
1.1 | 2 | 1 | 2 | 1 | 1 | 1 | 1 | throw ArithmeticException |
1.0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
-1.0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 |
-1.1 | -2 | -1 | -1 | -2 | -1 | -1 | -1 | throw ArithmeticException |
-1.6 | -2 | -1 | -1 | -2 | -2 | -2 | -2 | throw ArithmeticException |
-2.5 | -3 | -2 | -2 | -3 | -3 | -2 | -2 | throw ArithmeticException |
-5.5 | -6 | -5 | -5 | -6 | -6 | -5 | -6 | throw ArithmeticException |
2. BigDecimal.setScale()
Java 官方API推荐使用新的 setScale(int newScale, RoundingMode roundingMode) 代替旧的 setScale(int newScale, int roundingMode)。
由于 BigDecimal 对象是不可变的(immutable),因此 setScale() 方法不会修改原来的 BigDecimal 对象值,这与 setX 的常规约定不同。相反,将会返回一个新的具有指定精度的 BigDecimal 对象,该对象不一定是新分配的,其非精度值由此 BigDecimal 的非精度值乘以或除以 10 的适当次幂来确定,以保持其整体值,如果精度值减少了,非精度值必须被除,并且该值可以更改,在这种情况下,将指定的舍入模式应用到除法中。
int decimal = 2;//保留小数位数 double d1 = 0.525; double d2 = 0.625; double d3 = 0.425; double d4 = 0.325; System.out.println(BigDecimal.valueOf(d1).setScale(decimal, RoundingMode.HALF_EVEN) + " == " + new BigDecimal(d1).setScale(decimal, RoundingMode.HALF_EVEN)); System.out.println(BigDecimal.valueOf(d2).setScale(decimal, RoundingMode.HALF_EVEN) + " == " + new BigDecimal(d2).setScale(decimal, RoundingMode.HALF_EVEN)); System.out.println(BigDecimal.valueOf(d3).setScale(decimal, RoundingMode.HALF_EVEN) + " == " + new BigDecimal(d3).setScale(decimal, RoundingMode.HALF_EVEN)); System.out.println(BigDecimal.valueOf(d4).setScale(decimal, RoundingMode.HALF_EVEN) + " == " + new BigDecimal(d4).setScale(decimal, RoundingMode.HALF_EVEN)); /* * output * 0.52 == 0.53 * 0.62 == 0.62 * 0.42 == 0.42 * 0.32 == 0.33 */
这里有一个问题:new BigDecimal(double val),其结果有一定的不可预知性,导致结果不准确,应该使用 new BigDecimal(String val) 或 new BigDecimal(Double.toString(val)),BigDecimal提供了一个静态的valueOf(double val) 方法快速完成这件事情。
System.out.println(new BigDecimal(0.525d)); // output 0.52500000000000002220446049250313080847263336181640625 System.out.println(new BigDecimal("0.525")); // output 0.525 System.out.println(BigDecimal.valueOf(0.525)); // Double.toString(0.525)
// output 0.525
以上是关于BigDecimal舍入模式使用及建议的主要内容,如果未能解决你的问题,请参考以下文章