(这篇文章讲的很好,很正确,帮助你爬出坑!)
首先看篇Math.round的四舍五入
(放心里自己感受)
今天我们来谈一谈四舍五入的问题,有人就站起来了,四舍五入有啥问题啊,大兄弟,咱冷静点,我既然提出来了,那说明里面的坑还是不少的,针对Math.round等一些方法我们今天不做分析,可以大概看下上面贴出的文章链接,我们来谈一谈格式化BigDecimal和DecmalFormat,是不是很熟悉,肯定用过是吧,如果你用过,但是却发现没遇到过坑,那你来对了,太粗心了有木有,先给个案例给大家:
DecimalFormat decimalFormat = new DecimalFormat("#0.00");
Log.e("***",decimalFormat.format(1.125)+"---"+decimalFormat.format(1.135));
请问输出啥?
1.13—1.14
????????????
问题出来了吧,请看打印结果:
1.12—1.14
懵逼了吧,发现坑了吧,没关系,我们来看一看api。
1、ROUND_UP:远离零方向舍入。向绝对值最大的方向舍入,只要舍弃位非0即进位。
2、ROUND_DOWN:趋向零方向舍入。向绝对值最小的方向输入,所有的位都要舍弃,不存在进位情况。
3、ROUND_CEILING:向正无穷方向舍入。向正最大方向靠拢。若是正数,舍入行为类似于ROUND_UP,若为负数,舍入行为类似于ROUND_DOWN。Math.round()方法就是使用的此模式。
4、ROUND_FLOOR:向负无穷方向舍入。向负无穷方向靠拢。若是正数,舍入行为类似于ROUND_DOWN;若为负数,舍入行为类似于ROUND_UP。
5、 HALF_UP:最近数字舍入(5进)。这是我们最经典的四舍五入。
6、 HALF_DOWN:最近数字舍入(5舍)。在这里5是要舍弃的。
7、 HAIL_EVEN:银行家舍入法。
这是什么?这是格式化的舍入方式,长见识了吧,跟我们平时遇到的不大一样吧,默认使用的是第七种银行家,为什么会这么设计呢,因为很多四舍五入都用在了金融方面,金融方面一个小数点都能导致翻天覆地的变化,影响极其大。比如说采取我们正常的四舍五入方式,
四舍:0.000、0.001、0.002、0.003、0.004
五入:0.005、0.006、0.007、0.008、0.009
银行遇到四舍的数字就舍弃成0.000,相当于赚了用户0.001、0.002、0.003、0.004相加的值,遇到五入的数字就进位成0.010,相当于亏了0.005、0.004、0.003、0.001相加的值,最后
0.001+0.002+0.003+0.004-0.005-0.004-0.003-0.002-0.001=0.005
别小看这0.005,基本上每十笔交易就亏损0.005,这是个很大的数字,所以才会出现银行家射入法:
舍去位的数值小于5时,直接舍去。 舍去位的数值大于5时,进位后舍去。
当舍去位的数值等于5时,若5后面还有其他非0数值,则进位后舍去,若5后面是0时,则根据5前一位数的奇偶性来判断,奇数进位,偶数舍去。
对于上面的规则我们举例说明
11.556 = 11.56 ——六入
11.554 = 11.55 —–四舍
11.5551 = 11.56 —–五后有数进位
11.545 = 11.54 —–五后无数,若前位为偶数应舍去
11.555 = 11.56 —–五后无数,若前位为奇数应进位、
这样就能有效地避免以上问题的发生。
于是上面的测试改成了如下:
DecimalFormat decimalFormat = new DecimalFormat("#0.00");
decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
Log.e("***", decimalFormat.format(1.125) + "---" + decimalFormat.format(1.135));
你说HALF_UP就是我们平时那种四舍五入的方式,那我们就测试下看看。
1.13—1.14
ok,果然对了,是不是到这就结束了呢,别急,还有坑呢。
DecimalFormat decimalFormat = new DecimalFormat("#0.00"); decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
Log.e("***", decimalFormat.format(1.125f) + "---" + decimalFormat.format(1.135f));
这不一样吗,不就是换成了浮点型吗?有啥区别啊,请看打印日志:
1.13—1.13
刺激不刺激?不信?自己试试啊!别怕,带你分析分析。
首先我们进入format源码
public final String format(double value) {
return format(value, new StringBuffer(), new FieldPosition(0)).toString();
}
人家只支持double类型的,但是我写float类型也不会报错,他会默认将float类型转为double类型,强转的话很容易导致进度丢失。所以才会出现我们上面这种情况,那么该如何解决呢?请看:
BigDecimal d = new BigDecimal(String.valueOf(1.125f));
BigDecimal r = new BigDecimal(String.valueOf(1.135f));
DecimalFormat decimalFormat = new DecimalFormat("#0.00");
decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
Log.e("***", Float.parseFloat(decimalFormat.format(d.doubleValue())) + "-----" + Float.parseFloat(decimalFormat.format(r.doubleValue())));
我们引入了BigDecimal来将float转为double型,
public BigDecimal(String val) {
this(val.toCharArray(), 0, val.length());
}
BigDecimal支持字符串类型,不会失精度,我们看下输出结果:
1.13—–1.14
ok,四舍五入的坑就讲到这里!