Oracle SQL四舍五入行为

Sim*_*ley 4 sql oracle casting rounding

我遇到了binary_double使用Oracle SQL 进行四舍五入的奇怪行为。binary_double值应half even根据文档进行四舍五入,但是在通过以下查询进行测试时,似乎存在一些不一致之处。下面的所有查询都应分别使用相同的最后一位数字,即0.x00008和0.x00006(四舍五入为6位数字)或0.x0008和0.x0006(四舍五入为5位数字),且x in(0,1, 2,3,4,5,6,7,8,9)。问题是他们没有。希望对理解四舍五入的结果为何取决于分隔点之后的第一位和/或原始数字中的位数有帮助。

select 1,(round( cast (0.0000075 as binary_double ) ,6)), (round( cast (0.0000065 as binary_double ) ,6)) from dual
  union
  select 2,(round( cast (0.1000075 as binary_double ) ,6)), (round( cast (0.1000065 as binary_double ) ,6)) from dual
  union
  select 3,(round( cast (0.2000075 as binary_double ) ,6)), (round( cast (0.2000065 as binary_double ) ,6)) from dual
  union
  select 4,(round( cast (0.3000075 as binary_double ) ,6)), (round( cast (0.3000065 as binary_double ) ,6)) from dual
  union
  select 5,(round( cast (0.4000075 as binary_double ) ,6)), (round( cast (0.4000065 as binary_double ) ,6)) from dual
  union
  select 6,(round( cast (0.5000075 as binary_double ) ,6)), (round( cast (0.5000065 as binary_double ) ,6)) from dual
  union
  select 7,(round( cast (0.6000075 as binary_double ) ,6)), (round( cast (0.6000065 as binary_double ) ,6)) from dual
  union
  select 8,(round( cast (0.7000075 as binary_double ) ,6)), (round( cast (0.7000065 as binary_double ) ,6)) from dual
  union
  select 9,(round( cast (0.8000075 as binary_double ) ,6)), (round( cast (0.8000065 as binary_double ) ,6)) from dual
  union
  select 10,(round( cast (0.9000075 as binary_double ) ,6)), (round( cast (0.9000065 as binary_double ) ,6)) from dual
  union
  select 11,(round( cast (0.000075 as binary_double ) ,5)), (round( cast (0.000065 as binary_double ) ,5)) from dual
  union
  select 12,(round( cast (0.100075 as binary_double ) ,5)), (round( cast (0.100065 as binary_double ) ,5)) from dual
  union
  select 13,(round( cast (0.200075 as binary_double ) ,5)), (round( cast (0.200065 as binary_double ) ,5)) from dual
  union
  select 14,(round( cast (0.300075 as binary_double ) ,5)), (round( cast (0.300065 as binary_double ) ,5)) from dual
  union
  select 15,(round( cast (0.400075 as binary_double ) ,5)), (round( cast (0.400065 as binary_double ) ,5)) from dual
  union
  select 16,(round( cast (0.500075 as binary_double ) ,5)), (round( cast (0.500065 as binary_double ) ,5)) from dual
  union
  select 17,(round( cast (0.600075 as binary_double ) ,5)), (round( cast (0.600065 as binary_double ) ,5)) from dual
  union
  select 18,(round( cast (0.700075 as binary_double ) ,5)), (round( cast (0.700065 as binary_double ) ,5)) from dual
  union
  select 19,(round( cast (0.800075 as binary_double ) ,5)), (round( cast (0.800065 as binary_double ) ,5)) from dual
  union
  select 20,(round( cast (0.900075 as binary_double ) ,5)), (round( cast (0.900065 as binary_double ) ,5)) from dual;
Run Code Online (Sandbox Code Playgroud)

底线是:为什么在以下查询中,两个值之间存在差异:

SELECT (round( CAST (0.0000065 AS BINARY_DOUBLE ) ,6)), (round( cast (0.1000065 as binary_double ) ,6)) FROM dual;
Run Code Online (Sandbox Code Playgroud)

按照@zerkms的建议,我数字转换为二进制格式,然后得到:

0.0000065 -> 6.49999999999999959998360846147E-6
0.1000065 -> 1.00006499999999998173905169097E-1
Run Code Online (Sandbox Code Playgroud)

查询将其四舍五入至6位数字。令我惊讶的是,我发现四舍五入导致:

0.0000065 -> 0.000006 (execute the query above to see this)
0.1000065 -> 0.100007 (execute the query above to see this)
Run Code Online (Sandbox Code Playgroud)

这是为什么?我可以理解,如果我尝试四舍五入为> 12位数字,那么二进制表示形式中的数字系列开始有所不同,但是在这么早的阶段,这种差异怎么会变得明显呢?

zer*_*kms 5

让我们看一下第一个示例,因为其他示例非常相似:

0.0000075双精度IEEE 754是作为7.50000000000000019000643072808E-6

0.0000065是作为6.49999999999999959998360846147E-6

当您将两者都舍入为6时-前者变为8e-6,后者6e-6

没有“一致”的行为,因为将不同的数分解为2的除数是不同的。

因此,即使您这样做SELECT 0.0000065 FROM DUAL并看到0.0000065结果-并不是以二进制形式在内部表示它,它已经被“破坏”了,并且比那个数字少一小部分。然后在输出格式化期间为您取整。

双IEEE 754提供15-16个有效数字。因此,出于输出目的,它们变为:7.500000000000000e-6并且6.499999999999999e-6四舍五入为6.5e-6

UPD

6.49999999999999959998360846147E-6== 0.00000649999999999999959998360846147。如果将其四舍五入-等于0.000006,因为它后面跟着的4是小于5

1.00006499999999998173905169097E-1== 0.100006499999999998173905169097舍入为6的四舍五入0.100006,因为下一位是4,小于5。而且我看到了与实际结果的差异。老实说,我在这里没有很好的解释。我怀疑这是一个Oracle问题,因为:

UPD 2

在通过Skype聊天与同事进行了更多研究之后,我得到了一个很好的例子,其结果取决于所选的舍入模式:

flock.core> (import '[org.apache.commons.math3.util Precision])

flock.core> (Precision/round (Double. 0.1000065) 6 BigDecimal/ROUND_CEILING)
0.100007
flock.core> (Precision/round (Double. 0.1000065) 6 BigDecimal/ROUND_DOWN)
0.100006
flock.core> (Precision/round (Double. 0.1000065) 6 BigDecimal/ROUND_UP)
0.100007
flock.core> (Precision/round (Double. 0.1000065) 6 BigDecimal/ROUND_HALF_DOWN)
0.100006
flock.core> (Precision/round (Double. 0.1000065) 6 BigDecimal/ROUND_HALF_EVEN)
0.100006
flock.core> (Precision/round (Double. 0.1000065) 6 BigDecimal/ROUND_HALF_UP)
0.100007
flock.core> (Precision/round (Double. 0.1000065) 6 BigDecimal/ROUND_FLOOR)
0.100006
Run Code Online (Sandbox Code Playgroud)

结论

在这种情况下,没有“正确”或“不正确”的结果,它们都是正确的,并且在很大程度上取决于实现方式(执行算术运算时使用的+选项)。

参考文献:

  • @Simon Righley:根据该文档,它必须是“ 0.100006”。在Go和python上添加了示例,这些示例也返回了预期值。因此,我怀疑这只是Oracle公司的实现,都会影响jvm和oracle dbms。 (2认同)