javabigdecimal负数转正数_java的四舍五⼊(四舍六⼊?)舍
⼊模式详解
在开发中,保留N位⼩数位的需求肯定不会少。我们项⽬也有很多地⽅有这要求,其中⼀个地⽅是必须⽤到 freemarker的保留2位⼩数位即${x?string("0.##")} 语法,⽽这就是bug的源点。
场景重现
某⼀天测试突然提了个bug给我,没有⼈会乐意别⼈给⾃⼰提bug。⽽且⼀看这bug还是四舍五⼊的问题,这就是使⽤的框架⾃带的⽅法,怎么会存在问题呢?⼀看详情,确实是框架的⽅法存在问题,⼀看源码,直接调⽤的 JDK 的⽅法,这难道还是 JDK 存在问题?继续查下去发现事情并没有这简单。
四舍五⼊
我们知道,保留2位⼩数⼀般使⽤如下⼏种⽅法(为避免直接使⽤ double 出现精度问题,使⽤ String 转 BigDecimal 来操作):
public static void main(String[] args) {
String[] nums = new String[]{"1.114", "2.225", "3.336"};
scaleByRound(nums);
scaleByStringFormat(nums);
scaleByDecimalFormat(nums);
scaleByBigDecimal(nums);
}
// 第⼀种⽅法先扩⼤100倍然后四舍五⼊取值,然后再缩⼩100倍。
public static void scaleByRound(String[] nums) {
System.out.println("--------第⼀种⽅法--------");
Arrays.stream(nums).forEach(o ->
System.out.println(o+"-> "+(double) und(new BigDecimal(o).doubleValue() * 100) / 100)
);
}
// 使⽤String#format
public static void scaleByStringFormat(String[] nums) {
System.out.println("--------第⼆种⽅法--------");
Arrays.stream(nums).forEach(o ->
System.out.println(o+"-> "+String.format("%.2f", new BigDecimal(o)))
);
}
// 使⽤DecimalFormat#format
public static void scaleByDecimalFormat(String[] nums) {
System.out.println("--------第三种⽅法--------");
DecimalFormat df = new DecimalFormat("#.00");
Arrays.stream(nums).forEach(o ->
System.out.println(o+"-> "+df.format(new BigDecimal(o)))
);
}
// 使⽤BigDecimal#setScale
public static void scaleByBigDecimal(String[] nums) {
System.out.println("--------第四种⽅法--------");
Arrays.stream(nums).forEach(o -> {
bigdecimal除法保留小数BigDecimal num = new BigDecimal(o).setScale(2,BigDecimal.ROUND_HALF_UP);
System.out.println(o+"-> "+String());
});
}
运⾏结果在情理之中,⼜在意料之外:
--------第⼀种⽅法--------
1.114-> 1.11
2.225-> 2.23
3.336-> 3.34
--------第⼆种⽅法--------
1.114-> 1.11
2.225-> 2.23
3.336-> 3.34
-
-------第三种⽅法--------
1.114-> 1.11
2.225-> 2.22
3.336-> 3.34
--------第四种⽅法--------
1.114-> 1.11
2.225-> 2.23
3.336-> 3.34
结果显⽽易见,⼤部分都符合我们的预期。但是中间好像混进了什么,为什么第三种⽅法 2.225 保留2位⼩数是 2.22?这就让引出了 四舍六⼊五留双(银⾏家舍⼊法).
四舍六⼊五留双
四舍六⼊五留双起源于银⾏家舍⼊法,简单的四舍五⼊太过简单粗暴,对于⼤量使⽤的场景来说,会产⽣⼤量误差,⽐如银⾏。
⽐如保留两位⼩数:
需要舍的场景 0.000、0.001、0.002、0.003、0.004 总舍了:0.01
需要⼊的场景 0.005、0.006、0.007、0.008、0.009 总⼊了:0.15
可以看到⼊的⽐舍的多,长此以往并且在⼤体量数据下,则肯定造成客户或者银⾏损失。
为了使舍和⼊能达到相互平衡,关键就在于 0.005 不能直接⼊,⽽是也要⼀定的⼏率⼊或者舍。所以银⾏家规定:为5时,当5后⾯还有数字时(全0不算),则进⼀位。如果5后⾯没有数字了,则判断前⼀位数字,为奇数则进位,为偶数则舍去。
总结即为:四舍六⼊五考虑,五后⾮空就进⼀,五后为空看奇偶,五前为偶应舍去,五前为奇要进⼀。
⽐如保留两位⼩数:
1.114 = 1.11 ----> 四舍
1.116 = 1.12 ----> 六⼊
2.2251 = 2.23 ----> 五后⾮空就进⼀
2.225 = 2.22 ----> 五前为偶应舍去
2.235 = 2.24 -----> 五前为奇要进⼀
java⽀持的舍⼊⽅法
除了最常⽤的四舍五⼊还是上⾯提到的四舍六⼊五留双,java还⽀持多种舍⼈⽅法。所以类型在枚举类 RoundingMode,当然⾥⾯的值还是⽤的 BigDecimal 类静态常量。DecimalFormat 的默认舍⼊规则是 HALF_EVEN。
1. ROUND_UP:远离零⽅向舍⼊。正数更⼤,负数更⼩,远离0。
2. ROUND_DOWN:趋向零⽅向舍⼊。正数更⼩,负数更⼤,靠近0。
3. ROUND_CEILING:趋向正⽆穷⽅向舍⼊。正数、负数都是更⼤,正数时类似 ROUND_UP,负数时类似 ROUND_DOWN。
Math#round ⽅法使⽤的该舍⼊模式。
4. ROUND_FLOOR:趋向负⽆穷⽅向舍⼊。正数、负数都是更⼩,正数时类似 ROUND_DOWN,负数时类似 ROUND_UP
5. ROUND_HALF_UP:趋向最接近数字⽅向舍⼊的舍⼊模式(五⼊)。四舍五⼊。
6. ROUND_HALF_DOWN:趋向最接近数字⽅向舍⼊的舍⼊模式(五舍)。五舍六⼊。
7. ROUND_HALF_EVEN:趋向最接近数字⽅向舍⼊的舍⼊模式(五留双)。银⾏家舍⼊法
8. ROUND_UNNECESSARY:断⾔使⽤的,如果需要舍⼊操作,则抛异常。
直接列出 Java 源码中注释的例⼦:
每个舍⼊模式实例
保留位
经常我们有很多需求,⽐如千分位显⽰⾦额,⾦额强制保留两位⼩数,不⾜补0。下⾯探讨⼀下常⽤的事项⽅法。第⼀种⽅法逗号分隔显⽰,保留2位⼩数(四舍五⼊),不⾜补0.
DecimalFormat df = new DecimalFormat("###,##0.00");
BigDecimal bigDecimal = new BigDecimal("12312300.235").setScale(2,BigDecimal.ROUND_HALF_UP);
System.out.println(df.format(bigDecimal));
BigDecimal bigDecimal1 = new BigDecimal("12312300.2").setScale(2,BigDecimal.ROUND_HALF_UP);
System.out.println(df.format(bigDecimal1));
DecimalFormat df1= new DecimalFormat("###,##0.##");
System.out.println(df1.format(bigDecimal1));
DecimalFormat df = new DecimalFormat("###,##0.00"); #代表有则显⽰没有则不显⽰,⽽0则说明强制显⽰,没有则⽤0填充。
运⾏结果为
12,312,300.24
12,312,300.20
12,312,300.2
第⼆种⽅法
保留2位⼩数(四舍五⼊),不⾜补0。
System.out.println(String.format("%.2f", new BigDecimal("1.223")));
System.out.println(String.format("%.02f", new BigDecimal("1.223")));
System.out.println(String.format("%.02f", new BigDecimal("1.2")));
System.out.println(String.format("%.03f", new BigDecimal("1")));
%.2f: %. 表⽰ ⼩数点前任意位数 , 2 表⽰两位⼩数, 格式后的结果为f 表⽰浮点型。%.02f: %. 表⽰ ⼩数点前任意位数,0说明不⾜时补0, 2 表⽰两位⼩数, 格式后的结果为f 表⽰浮点型。
运⾏结果为
1.22
1.22
1.20
1.000
总结
从⼩学就学了四舍五⼊,但是⽣活中舍⼊的场景不仅只有四舍五⼊,向上、向下、更平衡的银⾏家。。。这些舍⼊模式Java都⽀持,科学在发展,社会在进步,java也⼀直与时俱进,我们作为Java的直接使⽤者也需不断学习,紧跟时代步伐。话说Java都快出16了......
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论