SQL中Round函数没有四舍五入

先来看下面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

有图有真相

SQL中Round函数没有四舍五入

SQL中Round函数没有四舍五入

SQL中Round函数没有四舍五入

SQL中Round函数没有四舍五入

SQL中Round函数没有四舍五入

SQL中Round函数没有四舍五入

问题:

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;

到这里还没有完,聪明的你会发现:

SQL中Round函数没有四舍五入

SQL中Round函数没有四舍五入

为什么这两个执行的结果不一致。

原因二:

查看了官方文档的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函数。

执行结果如下:

SQL中Round函数没有四舍五入

愿你遍历山河 仍觉人间值得

上一篇:[LeetCode] 319. Bulb Switcher


下一篇:Google Code Jam 2020 Round 1A