【问题标题】:Oracle Number Rouding IssueOracle 数字舍入问题
【发布时间】:2016-06-14 22:07:37
【问题描述】:

我们需要四舍五入以下号码:

28.854623

所以,“预期”的步骤是:

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

如果我们执行 3 位小数,结果如预期:

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)

谢谢!

【问题讨论】:

  • The documentation explains the algorithm used。它正在做它应该做的事情。您确定要/需要使用非传统方法吗?
  • 为什么这个标签是 Java 和 BigDecimal?我已经删除了这些标签 - 如果它们对您很重要,请重新编辑它们(并解释原因)。
  • 通常舍入的目的是减少精度位数,同时最小化引入的总体误差。您的迭代舍入方法会引入更大的总体误差而没有任何好处。

标签: oracle rounding


【解决方案1】:

我所见过的任何舍入函数都不会从最后一位小数开始并像这样向后工作。舍入函数将比您所需的十进制计数多看 1 个字符(如果找到的第一个字符是 5,则可能再多 1 个字符 MT0 澄清说,如果下一个字符是 5,Oracle 从 0 开始舍入)。

在您的示例中,ROUND(28.854623, 2) 会查看 28.854 并说“向下舍入”。行为如我所料。

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

【讨论】:

  • 如果您舍入到N 小数位并且N+1th 是5,那么ROUND() 将始终从零舍入。您无需查看下一位小数。
  • 你好@Tyler。正如我们在学校学到的那样,我一直认为 ROUND 函数(Oracle 和 Java 中的)在考虑所有重要的小数位时都可以工作。他的解释澄清并表明这种方法没有多大意义。谢谢你的帮助。
  • @MT0 很高兴知道。我不确定它是向上还是圆形。感谢您的澄清。
  • @regisxp Java 也将按照我的描述进行舍入。
【解决方案2】:

@Tyler 描述了ROUND() 的工作原理,算法在Oracle documentation 中有详细说明。

ROUND 使用以下规则实现:

  1. 如果 n 为 0,那么 ROUND 总是返回 0 而不管整数。

  2. 如果 n 为负数,则 ROUND(n, integer) 返回 -ROUND(-n, integer)。

  3. 如果n是正数,那么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 会向上舍入)。

【讨论】:

  • 你好@MT0。非常感谢您提供了一个示例,说明我们如何才能达到预期的结果。这很鼓舞人心。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2020-09-17
  • 1970-01-01
相关资源
最近更新 更多