课程咨询 :186 8716 1620      qq:2066486918

昆明Java培训 > 达内新闻 > 不要让四舍五入亏了一方
  • 不要让四舍五入亏了一方

    发布:昆明Java培训      来源:达内新闻      时间:2016-09-30

  • 昆明Java培训的老师建议还是来重温一个小学数学问题:四舍五入。四舍五入是一种近似精确的计算方法,在Java5之前,我们一般是通过Math.round来获得指定精度的整数或小数的,这种方法使用非常广泛,代码如下:

    public class Client25 {

    public static void main(String[] args) {

    System.out.println("10.5近似值: "+Math.round(10.5));

    System.out.println("-10.5近似值: "+Math.round(-10.5));

    }

    }

    输出结果为:10.5近似值: 11      -10.5近似值: -10

    这是四舍五入的经典案例,也是初级面试官很乐意选择的考题,绝对值相同的两个数字,近似值为什么就不同了呢?这是由Math.round采用的舍入规则决定的(采用的是正无穷方向舍入规则),我们知道四舍五入是由误差的:其误差 是舍入的一半。我们以舍入运用最频繁的银行利息计算为例来阐述此问题。

    我们知道银行的盈利渠道主要是利息差,从储户手里收拢资金,然后房贷出去,期间的利息差额便是所获得利润,对一个银行来说,对付给储户的利息计算非常频繁,人民银行规定每个季度末月的20日为银行结息日,一年有4次 结息日。

    场景介绍完毕,我们回头来看看四舍五入,小于5的数字被舍去,大于5的数字进位后舍去,由于单位上的数字都是自然计算出来的,按照利率计算可知,被舍去的数字都分布在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、.0004、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千万储户的银行家来说(对国内银行来说,5千万是个小数字),每年仅仅因为四舍五入的误差而损失的金额是:

    银行账户数量(5千万)*4(一年计算四次利息)*0.0005(每笔利息损失的金额)

    5000*10000*0.0005*4=100000.0;即,每年因为一个算法误差就损失了10万元,事实上以上的假设条件都是非常保守的,实际情况可能损失的更多。那各位可能要说了,银行还要放贷呀,放出去这笔计算误差不就抵消了吗?不会抵消,银 的贷款数量是非常有限的其数量级根本无法和存款相比。

    这个算法误差是由美国银行家发现的(那可是私人银行,钱是自己的,白白损失了可不行),并且对此提出了一个修正算法,叫做银行家舍入(Banker's Round)的近似算法,其规则如下:

    舍去位的数值小于5时,直接舍去;

    舍去位的数值大于等于6时,进位后舍去;

    当舍去位的数值等于5时,分两种情况:5后面还有其它数字(非0),则进位后舍去;若5后面是0(即5是最后一个数字),则根据5前一位数的奇偶性来判断是否需要进位,奇数进位,偶数舍去。

    以上规则汇总成一句话:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。我们举例说明,取2位精度;

    round(10.5551) = 10.56  round(10.555) = 10.56  round(10.545) = 10.56 

    要在Java5以上的版本中使用银行家的舍入法则非常简单,直接使用RoundingMode类提供的Round模式即可,示例代码如下:

    1 import java.math.BigDecimal;

    2 import java.math.RoundingMode;

    3

    4 public class Client25 {

    5    public static void main(String[] args) {

    6        //存款

    7        BigDecimal d = new BigDecimal(888888);

    8        //月利率,乘3计算季利率

    9        BigDecimal r = new BigDecimal(0.001875*3);

    10        //计算利息

    11        BigDecimal i =d.multiply(r).setScale(2,RoundingMode.HALF_EVEN);

    12        System.out.println("季利息是:"+i);

    13        

    14    }

    15 }

    在上面的例子中,我们使用了BigDecimal类,并且采用了setScale方法设置了精度,同时传递了一个RoundingMode.HALF_EVEN参数表示使用银行家法则进行近似计算,BigDecimal和RoundingMode是一个绝配,想要采用什么方式使用RoundingMode设置即可。 目前Java支持以下七种舍入方式:

    ROUND_UP:原理零方向舍入。向远离0的方向舍入,也就是说,向绝对值最大的方向舍入,只要舍弃位非0即进位。

    ROUND_DOWN:趋向0方向舍入。向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培训的老师提醒注意:根据不同的场景,慎重选择不同的舍入模式,以提高项目的精准度,减少算法损失。

    推荐文章

上一篇:java程序:边界还是边界

下一篇:提防包装类型的null值

最新开班日期  |  更多

Java--零基础全日制班

Java--零基础全日制班

开班日期:11/30

Java--零基础业余班

Java--零基础业余班

开班日期:11/30

Java--周末提升班

Java--周末提升班

开班日期:11/30

Java--零基础周末班

Java--零基础周末班

开班日期:11/30

  • 网址:http://km .java.tedu.cn      地址:昆明市官渡区春城路62号证券大厦附楼6楼
  • 课程培训电话:186 8716 1620      qq:2066486918    全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内国际公司(TARENA INTERNATIONAL,INC.) 版权所有 京ICP证08000853号-56