提高你的Java代码质量吧:不容忽视的四舍五入细节
2013-07-23 09:37
134 查看
一、分析
在许多数学计算的场景中,会用到近似取值的计算方法。常用的近似取值有四舍五入。
但是在某些金融行业或特殊场景中,四舍五入的方式就不太适宜。目前Java支持一下其中舍入方式:
ROUND_UP:远离零方向舍入,向远离0的方向舍入,也就是说,向绝对值最大的方向舍入,只要舍弃位非0即进位。
ROUND_DOWN:趋向零方向的舍入,向0方向靠拢,也就是说,向绝对值最小的方向输入。注意,所有的位都舍弃不存在进位的情况。
ROUND_CEILING:向正无穷方向舍入,向正最大方向靠拢,如果是正数,舍入行为类似于ROUND_UP;如果是负数,则舍入行为类似于ROUND_DOWN。注意,Math.round方法使用的即此模式。
ROUND_FLOOR:向负无穷方向舍入,向负无穷方向靠拢,如果是正数,则舍入行为类似于ROUND_DOWN;如果是负数,则舍入行为类似于ROUND_UP。
HALF_UP:最近数字舍入(5进),这就是我们最经典的四舍五入模式。
HALF_DOWN:最近数字舍入(5舍),在四舍五入中,5是进位的,而在HALF_DOWN中却是舍弃不进位。
HALF_EVEN:银行家算法,四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。
二、场景
使用Math.round来指定精度的整数或小数:
[java]
view plaincopyprint?
public class Client{
public static void main(String[] args){
System.out.println("10.5的近似值:" + Math.round(10.5));
System.out.println("-10.5的近似值:" + Math.round(-10.5));
}
}
由于Math.round采用的舍入规则是正无穷方向舍入。所以输出结果为:
10.5近似值:11
-10.5近似值:-10
但是在银行计算利息的场景下,适应四舍五入计算利息就会出现如下情况:
按照概率计算克制,被舍入的数字均匀的分布在0到9之间,下面以10笔存款利息计算作为模型,以银行家的身份来思考这个算法:
四舍:舍弃的数值:0.000、0.001、0.002、0.003、0.004,因为是舍弃的,对于银行家来说,就不用付给储户利息了,那每舍弃一个数字就会赚取相应的金额:0.000、0.001、0.002、0.003、0.004。
五入:进位的数值:0.005、0.006、0.007、0.008、0.009,因为是进位,对于银行家来说,每进一位就会多付款给储户,也就是亏损了,那亏损的部分就是对应的10进制补数:0.005、0.004、0.003、0.002、0.001。
因为舍弃和进位的数字在0到9之间是均匀分布的,所以对于银行家来说,每10笔存款利息因采用四舍五入而获得的盈利是:
0.000+0.001+0.002+0.003+0.004-0.005-0.004-0.003-0.002-0.001=-0.005。
也就是说没10笔利息的计算中,就损失了0.005元,每笔利息计算损失0.0005元。对于一家银行(上亿储户)来说,这个误差造成的损失也不可小视觉。
这个误差是由美国的银行家发现的,所以提供出了一个修正算法,叫做银行家舍入的近似算法,其规则如下:
舍去的位数小于5时,直接舍去;
舍去的位数大于等于6时,进位后舍去;
当舍去的数值等于5时,分两种情况:
5后面还有其它数字(非0),则进位后舍去;
若5后面是0(即5是最后一个数字),则根据5前一位数的奇偶性来判断是否需要进位,奇数进位,偶数舍去。
以上规则汇总成一句话:四舍六入考虑五,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。
在Java5以上的版本中,使用银行家的舍入算法非常简单,直接使用RoundingMode类,提供Round模式即可,示例代码如下:
[java]
view plaincopyprint?
public class Client{
public static void main(String[] args){
//存款
BigDecimal d = new BigDecimal(88888);
//月利率,乘3计算季利率
BigDecimal r = new BigDecimal(0.001875*3);
//计算利息
BigDecimal i = d.mutiply(r)setScale(2,RoundingMode.HALF_EVEN);
System.out.println("季利息是:" + i);
}
} <SPAN style="LINE-HEIGHT: 17px; BACKGROUND-COLOR: transparent; FONT-FAMILY: Calibri, sans-serif; COLOR: windowtext; FONT-SIZE: 11pt"> </SPAN>
三、建议
根据不同的场景,慎重选择不同的舍入模式,以提高项目的精准度,减少算法损失。在大量与货币数字交互的项目中,一定要选择好近似的计算模式,尽量减少因算法不同而造成的损失。
本文由www.shihanwater.com转载
在许多数学计算的场景中,会用到近似取值的计算方法。常用的近似取值有四舍五入。
但是在某些金融行业或特殊场景中,四舍五入的方式就不太适宜。目前Java支持一下其中舍入方式:
ROUND_UP:远离零方向舍入,向远离0的方向舍入,也就是说,向绝对值最大的方向舍入,只要舍弃位非0即进位。
ROUND_DOWN:趋向零方向的舍入,向0方向靠拢,也就是说,向绝对值最小的方向输入。注意,所有的位都舍弃不存在进位的情况。
ROUND_CEILING:向正无穷方向舍入,向正最大方向靠拢,如果是正数,舍入行为类似于ROUND_UP;如果是负数,则舍入行为类似于ROUND_DOWN。注意,Math.round方法使用的即此模式。
ROUND_FLOOR:向负无穷方向舍入,向负无穷方向靠拢,如果是正数,则舍入行为类似于ROUND_DOWN;如果是负数,则舍入行为类似于ROUND_UP。
HALF_UP:最近数字舍入(5进),这就是我们最经典的四舍五入模式。
HALF_DOWN:最近数字舍入(5舍),在四舍五入中,5是进位的,而在HALF_DOWN中却是舍弃不进位。
HALF_EVEN:银行家算法,四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。
二、场景
使用Math.round来指定精度的整数或小数:
[java]
view plaincopyprint?
public class Client{
public static void main(String[] args){
System.out.println("10.5的近似值:" + Math.round(10.5));
System.out.println("-10.5的近似值:" + Math.round(-10.5));
}
}
public class Client{ public static void main(String[] args){ System.out.println("10.5的近似值:" + Math.round(10.5)); System.out.println("-10.5的近似值:" + Math.round(-10.5)); } }
由于Math.round采用的舍入规则是正无穷方向舍入。所以输出结果为:
10.5近似值:11
-10.5近似值:-10
但是在银行计算利息的场景下,适应四舍五入计算利息就会出现如下情况:
按照概率计算克制,被舍入的数字均匀的分布在0到9之间,下面以10笔存款利息计算作为模型,以银行家的身份来思考这个算法:
四舍:舍弃的数值:0.000、0.001、0.002、0.003、0.004,因为是舍弃的,对于银行家来说,就不用付给储户利息了,那每舍弃一个数字就会赚取相应的金额:0.000、0.001、0.002、0.003、0.004。
五入:进位的数值:0.005、0.006、0.007、0.008、0.009,因为是进位,对于银行家来说,每进一位就会多付款给储户,也就是亏损了,那亏损的部分就是对应的10进制补数:0.005、0.004、0.003、0.002、0.001。
因为舍弃和进位的数字在0到9之间是均匀分布的,所以对于银行家来说,每10笔存款利息因采用四舍五入而获得的盈利是:
0.000+0.001+0.002+0.003+0.004-0.005-0.004-0.003-0.002-0.001=-0.005。
也就是说没10笔利息的计算中,就损失了0.005元,每笔利息计算损失0.0005元。对于一家银行(上亿储户)来说,这个误差造成的损失也不可小视觉。
这个误差是由美国的银行家发现的,所以提供出了一个修正算法,叫做银行家舍入的近似算法,其规则如下:
舍去的位数小于5时,直接舍去;
舍去的位数大于等于6时,进位后舍去;
当舍去的数值等于5时,分两种情况:
5后面还有其它数字(非0),则进位后舍去;
若5后面是0(即5是最后一个数字),则根据5前一位数的奇偶性来判断是否需要进位,奇数进位,偶数舍去。
以上规则汇总成一句话:四舍六入考虑五,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。
在Java5以上的版本中,使用银行家的舍入算法非常简单,直接使用RoundingMode类,提供Round模式即可,示例代码如下:
[java]
view plaincopyprint?
public class Client{
public static void main(String[] args){
//存款
BigDecimal d = new BigDecimal(88888);
//月利率,乘3计算季利率
BigDecimal r = new BigDecimal(0.001875*3);
//计算利息
BigDecimal i = d.mutiply(r)setScale(2,RoundingMode.HALF_EVEN);
System.out.println("季利息是:" + i);
}
} <SPAN style="LINE-HEIGHT: 17px; BACKGROUND-COLOR: transparent; FONT-FAMILY: Calibri, sans-serif; COLOR: windowtext; FONT-SIZE: 11pt"> </SPAN>
public class Client{ public static void main(String[] args){ //存款 BigDecimal d = new BigDecimal(88888); //月利率,乘3计算季利率 BigDecimal r = new BigDecimal(0.001875*3); //计算利息 BigDecimal i = d.mutiply(r)setScale(2,RoundingMode.HALF_EVEN); System.out.println("季利息是:" + i); } }
三、建议
根据不同的场景,慎重选择不同的舍入模式,以提高项目的精准度,减少算法损失。在大量与货币数字交互的项目中,一定要选择好近似的计算模式,尽量减少因算法不同而造成的损失。
本文由www.shihanwater.com转载
相关文章推荐
- 提高你的Java代码质量吧:不容忽视的四舍五入细节
- 提高你的Java代码质量吧:谨慎包装类型的比较
- 提高你的Java代码质量吧:不要在常量和变量中出现易混淆的字母
- 提高你的Java代码质量吧:如果有必要,使用变长数组吧
- 提高你的Java代码质量吧:推荐使用枚举定义常量
- 提高你的Java代码质量吧:使用构造函数协助描述枚举项
- 提高你的Java代码质量吧:多种最值算法,适时选择
- 提高你的Java代码质量吧:推荐在复杂字符串操作中使用正则表达式 .
- 提高你的Java代码质量吧:中文字符串排序的瑕疵
- 提高你的Java代码质量吧:静态导入给你带来的麻烦
- 提高你的Java代码质量吧:字符拼接位置的玄机
- 提高你的Java代码质量吧:警惕数组的浅拷贝
- 提高你的Java代码质量吧:警惕数组的浅拷贝
- 提高你的Java代码质量吧:让我们疑惑的字符串拼接方式的选择
- 【如何提高Java代码质量:频繁插入和删除时使用LinkedList 】
- 提高Java代码质量的Eclipse插件之Checkstyle的使用详解
- 提高Java代码质量的Eclipse插件之Checkstyle的使用详解
- 提高你的Java代码质量吧:危险的边界
- 提高你的Java代码质量吧:推荐使用String直接量赋值