Oracle 号码路由问题

Oracle Number Rouding Issue

我们需要四舍五入以下数字:

28.854623

因此,'expected' 步骤是:

1) 28.854623
2) 28.85462
3) 28.8546
4) 28.855
5) 28.86

但是,ROUND 函数的结果似乎是错误的:

SELECT ROUND(28.854623, 2) FROM DUAL;
Result> 28.85

如果我们以小数点后三位执行,结果如预期:

SELECT ROUND(28.854623, 3) FROM DUAL;
Result> 28.855

如果我们ROUND到3个地方,再到2个地方,结果也是预期的:

SELECT ROUND (ROUND (28.854623, 3), 2) FROM DUAL
Result> 28.86

谁能解释一下为什么会出现这种行为? ROUND 函数不是在第 (N+1) 位小数位上进行 TRUNC,然后将结果四舍五入,如下所示:

ROUND(TRUNC(VALUE, N+1), N)

谢谢!

我所见过的舍入函数都不会从最后一位小数点开始并像这样倒退。舍入函数看起来比您要求的十进制数多 1 个字符(如果第一个找到的是 5,则可能再多 1 个字符 MT0 澄清说,如果下一个字符是 5,Oracle 会舍入 0 ).

在您的示例中,ROUND(28.854623, 2) 会看着 28.854 并说 'round down'。行为符合我的预期。

如果您希望函数像您描述的那样进行舍入,您可能需要自己实现一个。

@Tyler 描述了 ROUND() 的工作原理,算法在 Oracle documentation.

中有详细说明

ROUND is implemented using the following rules:

  1. If n is 0, then ROUND always returns 0 regardless of integer.

  2. If n is negative, then ROUND(n, integer) returns -ROUND(-n, integer).

  3. If n is positive, then ROUND(n, integer) = FLOOR(n * POWER(10, integer) + 0.5) * POWER(10, -integer)

如果你真的想像你描述的那样四舍五入,那么你可以创建自己的函数:

CREATE OR REPLACE FUNCTION round_strangely(
  value  IN NUMBER,
  places IN INT DEFAULT 0
) RETURN NUMBER DETERMINISTIC
AS
  shifted NUMBER      := ABS( value ) * POWER( 10, places );
BEGIN
  RETURN ( FLOOR( shifted )
           + CASE WHEN 9 * MOD( shifted, 1 ) > 4 THEN 1 ELSE 0 END
         )
         * SIGN( value )
         / POWER( 10, places );
END;
/

可以按照ROUND():

使用
WITH numbers ( value, dp ) AS (
SELECT 28.854623, 5 FROM DUAL UNION ALL
SELECT 28.854623, 4 FROM DUAL UNION ALL
SELECT 28.854623, 3 FROM DUAL UNION ALL
SELECT 28.854623, 2 FROM DUAL UNION ALL
SELECT 28.852623, 1 FROM DUAL
)
SELECT value,
       ROUND_STRANGELY( value, dp ) AS rounded
FROM   numbers;

并给出输出:

     VALUE    ROUNDED
---------- ----------
 28.854623   28.85462 
 28.854623    28.8546 
 28.854623     28.855 
 28.854623      28.86 
 28.852623       28.9 

但是您的算法所做的不是四舍五入,而是四分之四以上的向上舍入(因为 .4 会向下舍入,而 .444...445 会向上舍入)。