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:
If n is 0, then ROUND always returns 0 regardless of integer.
If n is negative, then ROUND(n, integer) returns -ROUND(-n, integer).
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 会向上舍入)。
我们需要四舍五入以下数字:
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:
If n is 0, then ROUND always returns 0 regardless of integer.
If n is negative, then ROUND(n, integer) returns -ROUND(-n, integer).
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 会向上舍入)。