2020-3-15 反馈原文
刚在Enjoy模板中需要使用除法计算,数据源Record中的2个都是小数(2位),html如下:
<tr> <th class="center aligned">合计</th> <th class="right aligned">#(sum.budget)</th> <th class="right aligned">#(sum.expense)</th> <th>#(sum.expense / sum.budget)</th> <th>#(10396.08 / 954095.26)</th> </tr>
如上,第4列中的sum.expense的值和sum.budget的值分别是10396.08 、954095.26,和第5列相等,但是渲染结果是:
合计 | 954095.26 | 10396.08 | 0.01 | 0.010896270462553183 |
---|
结果分别是0.01和0.010896270462553183。
这里#(sum.expense / sum.budget)总是得到2位小数,测试多次也是如此,但是直接用数字就可以得到足够小数位,想问一下Enjoy中的小数位数是如何默认的。
实际上想得到最少4位结果以便显示百分比:
#(sum.expense / sum.budget, "0.00%")。
---------------------------------------------------------------------
2020-3-25 测试结果反馈
根据James的提示,对Arith进行了调试。
不出所料,enjoy中对位数最主要影响的代码出现在div方法里。
private Number div(int maxType, Number left, Number right) { switch (maxType) { case INT: return Integer.valueOf(left.intValue() / right.intValue()); case LONG: return Long.valueOf(left.longValue() / right.longValue()); case FLOAT: return Float.valueOf(left.floatValue() / right.floatValue()); case DOUBLE: return Double.valueOf(left.doubleValue() / right.doubleValue()); case BIGDECIMAL: BigDecimal[] bd = toBigDecimals(left, right); // return (bd[0]).divide(bd[1]); return (bd[0]).divide(bd[1], RoundingMode.HALF_EVEN); // 银行家舍入法 } throw new TemplateException("Unsupported data type", location); }
从代码可以看出,最核心的结果是出现在第14行:
return (bd[0]).divide(bd[1], RoundingMode.HALF_EVEN);
这里BigDecimal的divide方法中,scale默认采用了bd[0]的scale:
Returns a BigDecimal whose value is (this / divisor), and whose scale is this.scale(). If rounding must be performed to generate a result with the given scale, the specified rounding mode is applied.
因此,计算结果保留2位小数是可以预见的。
而且,结果的位数与格式化指令#number()是没关系的,因为#number()只是对结果进行了格式化,而无法影响devide的计算精度;结果只有2位小数的情况下,#number()只能格式化成1.00%。
第二,可以验证,#number(bd, "#.##%")与#number(bd, "0.00%")并非影响因素。
#number(0.012, "#.##%")会被格式化为1.2%;
#number(0.012, "0.00%")会被格式化为1.20%而已(用0补足位数)。
如下是调试截图:
第三,在enjoy中对所有bigDecimal采用half_even的舍入行为,而不是四舍五入,不知是否妥当,虽然该舍入方法在统计上更加精确(流行于美国)。
是否有更加灵活的方式可以指定精度或者舍入方法。。。
需要用到 #number 指令来输出,输出的格式仍然使用 java 原有规则,不增加任何学习成本,注意看 #number 指令:
https://jfinal.com/doc/6-4
由于 #() 输出指令不指定格式,所以就跟 System.out.println(...) 输出的行为一样