TRUNC 结果为 0 位小数的错误值

TRUNC results wrong value for 0 decimals

我搜索以下情况时没有找到答案。我有一个简单的值要四舍五入到 0 位小数。我在 oracle 中尝试以下查询并得到如下所示:

select 1 / 3 * 19608 CORRECT from dual;
--6536 is correct

select TRUNC(1 / 3 * 19608, 0) WRONG from dual;
 --6535 which is wrong

select TRUNC(1 / 3 * 19608, 2) WRONG from dual;
 --6535.99 which is wrong

如果您有与此情况相关的问题,请告诉我。


更新

很抱歉拒绝您的回答。我又发现了一个问题,您的回答仍然无效:

select TRUNC(1 / 3 * 15, 0) WRONG from dual;
--5 is correct

我得到的是小数字的正确数字。我也希望给出错误的结果,因为 19608 会导致错误的值。 19608 是一个神奇的数字吗?


更新 2

我理解下面答案中解释的数学。但我仍然怀疑 oracle TRUNC 函数没有按预期提供正确的输出。现在,我想用下面的例子来说明这一点:

SELECT ( (4500 + ROWNUM) * 3) multiple_3,
       TRUNC (1 / 3 * ( (4500 + ROWNUM) * 3), 2) result_with_trunc,
       1 / 3 * ( (4500 + ROWNUM) * 3) no_trunc
  FROM DUAL CONNECT BY 4500 + ROWNUM < 6000

上面SQL可以查看结果;恰好在 500 行之后,oracle 的行为与可被 3 整除的数字不同。 如果以下答案为真,则表示 15000 可以被 3 整除,而 15003 不能 可以被 3 整除。

我希望这与任何数学都无关。


更新 3

一个简单直接的问题可以如下:

SQL> set numwidth 50
SQL> SELECT TRUNC(1 / 3 * 15000, 50) FROM dual;

                               TRUNC(1/3*15000,50)
--------------------------------------------------
                                              5000

SQL> SELECT TRUNC(1 / 3 * 15003, 50) FROM dual;

                               TRUNC(1/3*15003,50)
--------------------------------------------------
         5000.999999999999999999999999999999999999

为什么我的 oracle 客户端 (11.2.0.1.0) 和服务器 (11g 11.2.0.4.0 - 64bit) 对可被 3 整除的数字进行相同的操作会给出不同的结果?


上次更新 - 已接受的答案

@Lalit Kumar B 感谢您强调循环小数。 我遇到的案例不是oracle的错。 trunc 正在正常工作。 问题是 0.3333.... 分数乘以可被 3 整除但不能完全整除的数字。此类数字的例子很少,如 300、15003 等,

因此,ORACLE TRUNC FUNCTION 的结果应用于与 1/3*X 相乘的小数时是正确的,其中 X 不能被 3 完全整除。

查询和输出都没有问题。这是您的 client,它是您第一个查询输出中值的四舍五入。

SQL> SELECT TRUNC(1 / 3 * 19608, 50) FROM dual;

TRUNC(1/3*19608,50)
-------------------
               6536

SQL> set numwidth 50
SQL> SELECT TRUNC(1 / 3 * 19608, 50) FROM dual;

                               TRUNC(1/3*19608,50)
--------------------------------------------------
         6535.999999999999999999999999999999999999

SQL>

在我的SQL*Plus客户端中,我调整了numwidth,你可以看到实际值。

此外,在 Oracle 中,对于 NUMBER 数据类型scale 有限制,您可以达到 100% 的准确性。这个限制是 38 位数字,post,你不可能有 100% 的准确性。您需要了解 mantissaexponent。在这种情况下,scale 限制了 exponent 的可能最小值。

更新 OP 更新了问题。

select TRUNC(1 / 3 * 15, 0) WRONG from dual; --5 is correct

与Oracle无关。它是纯数学15 可以被 3 整除,因此你得到一个 整数 5 作为结果。

Is 19608 a magic number ?

19608 不能完全整除 3,你将得到 小数部分。我已经在上面展示了。 (1/3)*19608 的正确结果是 NOT 6536。正确的结果是 6535.9 循环小数 。它永远不能被 3 整除。

更新 2 OP 更新了问题。

SQL> set numwidth 50
SQL> SELECT (1/3)*5000*3 FROM dual;

                                      (1/3)*5000*3
--------------------------------------------------
         5000.000000000000000000000000000000000001

SQL> SELECT (1/3)*(5000*3) FROM dual;

                                    (1/3)*(5000*3)
--------------------------------------------------
                                              5000

SQL> SELECT trunc((1/3)*(5000*3),2) FROM dual;

                           TRUNC((1/3)*(5000*3),2)
--------------------------------------------------
                                              5000

SQL> SELECT 1 / 3 * 15003 FROM dual;

                                         1/3*15003
--------------------------------------------------
         5000.999999999999999999999999999999999999

SQL> SELECT 15003/3 FROM dual;

                                           15003/3
--------------------------------------------------
                                              5001

上述similar(not same)操作的查询结果不同的原因是Oracle从左到右求值。

当您执行 1/3*n 时,Oracle 对它的评估不同于 n/3。在前一种情况下,首先评估 1/3,从而创建 recurring non-terminating decimal number 0.3333....,其中 3 是重复数字。现在,当你将它与一个 3 的倍数相乘时,它已经失去了 精度 ,因此最终结果也将是循环不终止数。

当你把它放在大括号内时,你是在要求 Oracle 显式计算大括号内的部分,而不仅仅是从左到右。因此,以下两个在 Oracle 中未计算

1 / 3 * 15003

的评估方式与

不同
15003/3