postgresql 向下取半函数

Ban*_*ank 6 postgresql floating-point rounding plpgsql floating-accuracy

PostgreSQL 中的 round(numeric,integer) 函数仅向上舍入:

round(cast (41.0255 as numeric),3) ==> 41.026
Run Code Online (Sandbox Code Playgroud)

由于我们需要一个返回 41.025 的 round 函数,并且(非常令人惊讶)PostgreSQL 中没有这样的函数(我们使用的是 9.1.5),因此我们编写了一个“包装器”函数,该函数在第一个版本中非常幼稚,并且粗糙......但由于 plpgsql 中缺乏对此类问题的本机支持,我们没有找到更好的东西。

代码如下所示。问题是它对于我们的目的来说太慢了。您能建议一个更好的方法来处理这项任务吗?

这是代码:

    CREATE OR REPLACE FUNCTION round_half_down(numeric,integer) RETURNS numeric 
    AS $$
    DECLARE
      arg ALIAS FOR $1;
      rnd ALIAS FOR $2;
      tmp1 numeric;
      res numeric;
    BEGIN
      tmp1:=arg;
      IF cast(tmp1 as varchar) ~ '5$'  THEN res:=trunc(arg,rnd);
      ELSE res:=round(arg,rnd);
      END IF;

      RETURN res;
    END;
    $$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

我需要转换数值并使用正则表达式......这就是(我认为)破坏性能的原因。

正如您所知:我们需要这个,因为我们必须比较存储在两个不同列(在两个不同表上)但具有不同数字数据类型的数字:一个是双精度数,一个是实数。问题是,当插入到真实数据类型列时,PostgreSQL 执行 ROUND HALF DOWN,但它没有通过其数学函数提供这样的选项!

编辑:
该功能实际上有问题。第一次快速重写是为了提高工作功能的性能,但速度非常慢。

行为必须符合以下条件:
IF舍入时推迟的小数位向上<=5 => trunc
ELSE舍入。

一些例子:

select round_half_down(cast (41.002555 as numeric),3) -- 41.002 
select round_half_down(cast (41.002555 as numeric),4) -- 41.0025 
select round_half_down(cast (41.002555 as numeric),5) -- 41.00255 
Run Code Online (Sandbox Code Playgroud)

而 PostgreSQL 中的 round 函数给出:

select round(cast (41.002555 as numeric),3) -- 41.003
Run Code Online (Sandbox Code Playgroud)

con*_*nit 5

方法非常快,无需创建新的 FUNCTION,该新的 FUNCTION 可以向下舍入一半,如下所示:

-- 向上舍入一半

round($n, 3)
Run Code Online (Sandbox Code Playgroud)

-- 向下舍入一半

round($n-0.5, 3)
Run Code Online (Sandbox Code Playgroud)


Erw*_*ter 3

我们必须比较存储在两个不同列(在两个不同表上)但具有不同数字数据类型的数字:一个是双精度数,一个是实数。

这应该非常快速和简单:

SELECT dp_col, real_col
FROM   tbl
WHERE  dp_col::real = real_col
Run Code Online (Sandbox Code Playgroud)

基本上,只需将double precision数字转换为real进行比较即可。


如果这对你不起作用,这个SQL 函数应该可以正常工作并且运行得更快

CREATE OR REPLACE FUNCTION round_half_down1(numeric, int)
  RETURNS numeric LANGUAGE sql AS
$func$
SELECT CASE WHEN abs($1%0.1^$2) < .6 * 0.1^$2 THEN
         trunc($1, $2)
    ELSE round($1, $2) END;
$func$
Run Code Online (Sandbox Code Playgroud)

现在修复了负数,并在注释中输入了 @sufleR 的内容。
您也可以只使用包含的CASE表达式。

% .. 模运算符
^ .. 求幂


这是一个可用于基准测试的快速测试:

SELECT n                                   -- Total runtime: 36.524 ms
      ,round_half_down1(n,3)               -- Total runtime: 70.493 ms
      ,round_down_to_decimal_places(n,3)   -- Total runtime: 74.690 ms
      ,round_half_down(n,3)                -- Total runtime: 82.191 ms
FROM  (SELECT random()::numeric AS n FROM generate_series(1,10000)) x
WHERE  round_down_to_decimal_places(n,3)
    <> round_half_down1(n,3)
Run Code Online (Sandbox Code Playgroud)

它还演示了 @Parveen 的函数和您编辑的版本如何计算错误 - 它们trunc()不应该计算错误。