先来看下面SQL执行结果:
select cast(CAST(ROUND(nvl(0.987689,0)*100,2) AS DECIMAL(4,2)) AS STRING) num;
select cast(CAST(ROUND(nvl(0.804889,0)*100,2) AS DECIMAL(4,2)) AS STRING) num;
select round(0.8048892988929889*100,2) num;
select cast(CAST(ROUND(nvl(0.805889,0)*100,2) AS DECIMAL(4,2)) AS STRING) num;
select CAST(ROUND(cast(nvl(0.8038892988929889,0) as DECIMAL(7,5))*100,2) as string) num;
select CAST(ROUND(cast(nvl(0.8048892988929889,0) as DECIMAL(7,5))*100,2) as string) num;
执行结果依次为:
98.77
80.48
80.49
80.59
80.49
注:使用的数据库是impala
有图有真相
问题:
0.987689乘100后(98.7689)保留两位小数,可以四舍五入,结果为:98.77;
0.805889乘100后(80.5889)保留两位小数,可以四舍五入,结果为:80.59;
0.803889乘100后(80.3889)保留两位小数,可以四舍五入,结果为:80.39;
但0.804889乘100后(80.4889)保留两位小数,没有四舍五入,结果为:80.48。
知道了问题,现在分析一下原因:
为什么我们在写sql使用round函数四舍五入时,0.804889乘100后保留两位小数,明明后面是8可以进位反而舍掉了呢?
原因一:
在于“四舍六入五成双”原则(来源于百度百科)
对于位数很多的近似数,当有效位数确定后,其后面多余的数字应该舍去,只保留有效数字最末一位,这种修约(舍入)规则是“四舍六入五成双”,也即“4舍6入5凑偶”,这里“四”是指≤4 时舍去,"六"是指≥6时进上,"五"指的是根据5后面的数字来定,当5后有数时,舍5入1;当5后无有效数字时,需要分两种情况来讲:
(1)5前为奇数,舍5入1;
(2)5前为偶数,舍5不进。
我理解的这里的“5”是指代比5大的数字。
上面这些的意思就是:
80.3889这里的8前面是3(奇数),所以舍8入1,就是9;
80.5889这里的8前面是5(奇数),所以舍8入1,就是9;
80.4889这里的8前面是4(偶数),所以舍8不进,还是8;
到这里还没有完,聪明的你会发现:
为什么这两个执行的结果不一致。
原因二:
查看了官方文档的Round函数说明:
For exact-value numbers, ROUND() uses the “round half up” rule(对于精确的数值, ROUND 函数使用四舍五入)
For approximate-value numbers, the result depends on the C library. On many systems, this means that ROUND() uses the “round to nearest even” rule: A value with any fractional part is rounded to the nearest even integer. (对于近似值,则依赖于底层的C函数库,在很多系统中 ROUND 函数会使用“取最近的偶数”的规则)
在计算机的计算领域里,包括JAVA里的double,float都不算是精准的计算类型,一般都用decimal进行精准计算,所以Round的计算就默认的走了第二条规则。
解决方案:
先采用CAST函数,用于将某种数据类型的表达式显式转换为另一种数据类型decimal。然后再ROUND函数。
执行结果如下:
愿你遍历山河 仍觉人间值得