函数返回给定数字的N个有效数字

时间:2018-11-06 22:35:00

标签: postgresql number-formatting to-char

我已经看到了有关此主题的几个问题,但是答案对我而言似乎没有用。

我必须在PostgreSQL 10.5中创建一个函数,该函数返回具有N个有效数字的数字。我已经针对此问题尝试了不同的解决方案,但是我遇到了特殊情况。在示例下面,其中nmNumber是输入参数的数量,nmSf是有效数字的数量。

SELECT round(nmNumber * power(10, nmSf-1-floor(log(abs(nmNumber ))))) / power(10, nmSf-1-floor(log(abs(nmNumber)))) result1,
       round(nmNumber, cast(-floor(log(abs(nmNumber))) as integer)) result2,
       floor(nmNumber / (10 ^ floor(log(nmNumber) - nmSf + 1))) * (10 ^ floor(log(nmNumber) - nmSf + 1)) result3;

如果nmNumber = 0.0801和nmSf = 2,则:

结果1 = 0.08; result2 = 0.08;结果3 = 0.08

鉴于以下三个结果,这是不正确的:

  1. 小数点后紧跟的零不是有效数字。
  2. 所有非零数字都是有效的。
  3. 十进制中除零以外的数字后面的零是有效的。

根据点3,上一个示例的正确结果是:0.080,而不是0.08,尽管从数学上看,它是相同的,但我必须从视觉上获得此结果。关于如何解决我的问题的一些想法?我想返回VARCHAR来交换NUMERIC是要提出的解决方案的一部分。

有些想法,或者我缺少一些东西。谢谢。

2 个答案:

答案 0 :(得分:0)

这将实现您的要求:

CREATE OR REPLACE FUNCTION f_significant_nr(_nr numeric, _sf int, OUT _nr1 text) AS
$func$
DECLARE
   _sign    bool;    -- record negative sign
   _int     int;     -- length of integral part
   _nr_text text;    -- intermediate text state
BEGIN
   IF _sf < 1 THEN   
      RAISE EXCEPTION '_sf must be > 0!';
   ELSIF _nr IS NULL THEN
      RETURN;  -- returns NULL
   ELSIF _nr = 0 THEN
      _nr1 := '0'; RETURN;
   ELSIF _sf >= length(translate(_nr::text, '-.','')) THEN -- not enough digits, optional shortcut
      _nr1 := _nr; RETURN;
   ELSIF abs(_nr) < 1  THEN
      _nr1 := COALESCE(substring(_nr::text, '^.*?[1-9]\d{' || _sf-1 || '}'), _nr::text); RETURN;
   ELSIF _nr < 0 THEN  -- extract neg sign
      _sign := true;
      _nr := _nr * -1;
   END IF;

   _int := trunc(log(_nr))::int + 1;         -- <= 0 was excluded above!

   IF _sf < _int THEN                        -- fractional digits not touched
      _nr1 := rpad(left(_nr::text, _sf), _int, '0');
   ELSE
      _nr1 := trunc(_nr)::text;
      IF _sf > _int AND (_nr % 1) > 0 THEN   -- _sf > _int and we have significant fractional digits
         _nr_text := right((_nr % 1)::text, -1);   -- remainder: ".123"
         _nr1 := _nr1 || COALESCE(substring(_nr_text, '^.*?[1-9]\d{' || (_sf - _int - 1)::text || '}'), _nr_text);
      END IF;
   END IF;

   IF _sign THEN
      _nr1 := '-' || _nr1;
   END IF;  
END
$func$  LANGUAGE plpgsql;

db <>提琴here -带有测试用例。

处理所有您要扔的东西,包括NULL00.000或负数。

但是我对您的第3点表示怀疑。

答案 1 :(得分:0)

postgres知道如何做到这一点(甚至是第3点),您只需要问对它正确的方式即可。

create or replace function 
   sig_fig(num numeric,prec int) returns numeric 
   language sql as
$$
  select to_char($1, '9.'||repeat('9',$2-1)||'EEEE')::numeric
$$;

创建一个格式字符串,产生科学的 带有3位数尾数的符号。用那个 将该数字转换为文本,然后将其转换回数字,从而产生具有许多有效数字的常规数字。

演示:

 with v as ( values (3.14159),(1234567),(0.02),(1024),(42),(0.0098765),(-6666) ) select column1 ,sig_fig(column1,3) as "3sf" from v;
  column1  |   3sf   
-----------+---------
   3.14159 |    3.14
   1234567 | 1230000
      0.02 |  0.0200
      1024 |    1020
        42 |    42.0
 0.0098765 | 0.00988
     -6666 |   -6670
(7 rows)