MENU

FreeMarker格式化数字的坑

April 15, 2018 • Read: 223 • 无码

周一下午群里有运营反馈导出的单据中总金额和每项目金额加起来对上,差了一毛钱;目测是我的问题,赶紧回了句“稍等,正在看。。。”就去看代码了,心想着这块应该不涉及四舍五入的问题,当时产品和各业务线约定的是一位小数,理论上不会出现问题;万万没想到,返回数据中有些是两位小数。

当时为了快速上线,代码写的不严谨,肯定是没保留一位小数先累加导致的;比如,三个100.04求和后变成了300.1,但每项都是100,就差了0.1。不过看了实际数据后,发现不是这个问题导致的;实际数据总共有三条100200.5100.05,页面显示的是100.0200.5100.0,总金额400.6

在FreeMark模版里都是这样格式化为一位小数的:

${xx?string("0.0")}

100.05显示成了100.0,感觉像是没有四舍五入而是直接截取的;总和在代码里计算结果是400.55页面显示为400.6,感觉又像进行了四舍五入。去FreeMark官网文档看了Built-ins for numbers中的string (when used with a numerical value)有以下示例:

官网示例代码:

${1.5?string["0"]} <-- 1.5, rounded towards even neighbor
${2.5?string["0"]} <-- 2.5, rounded towards even neighbor

示例代结果:

2 <-- 1.5, rounded towards even neighbor
2 <-- 2.5, rounded towards even neighbor

可以看出,1.5显示成了2,而2.5也显示成了2;rounded towards even neighbor,应该就是【四舍六入五成双】的计数保留法;对应java BigDecimal的HALF_EVEN

四舍六入五成双规则
  1. 被修约的数字小于5时,该数字舍去;
  2. 被修约的数字大于5时,则进位;
  3. 被修约的数字等于5时,要看5前面的数字,若是奇数则进位,若是偶数则将5舍掉,即修约后末尾数字都成为偶数;若5的后面还有不为“0”的任何数,则此时无论5的前面是奇数还是偶数,均应进位。

最后,尽量不使用默认行为,避免实际结果与期望结果不一样。像SimpleDateFormat、String这类在不指定Local或编码,结果会因为环境差异而不同。

如下代码,构造SimpleDateFormat未指定Local,在不同环境下得到了不同的结果。

SimpleDateFormat df = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE");
2018年04月15日 14:21:08 星期日

2018年04月15日 14:20:03 Sun
Tags: None
Archives QR Code
QR Code for this page
Tipping QR Code