Функция для возврата N значащих цифр данного номера - PullRequest
0 голосов
/ 07 ноября 2018

Я видел несколько вопросов на эту тему, но ответы не кажутся мне полезными.

Я должен создать функцию в 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; результат2 = 0,08; результат 3 = 0,08

Три результата неверны, учитывая, что:

  1. Нули сразу после десятичной точки не являются значащими цифрами.
  2. Все ненулевые цифры являются значимыми.
  3. Значимы нули после цифр, отличных от нуля в десятичной дроби.

Согласно пункту 3, правильный результат предыдущего примера: 0,080, а не 0,08, и хотя математически он оказывается одинаковым, визуально я должен получить этот результат. Некоторые идеи о том, как я могу решить мою проблему? Я полагаю, что возвращение VARCHAR в обмен на NUMERIC является частью предлагаемого решения.

Какая-то идея или я что-то упускаю. Спасибо.

Ответы [ 2 ]

0 голосов
/ 07 ноября 2018

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)
0 голосов
/ 07 ноября 2018

Это осуществит то, что вы просите:

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 <> скрипка здесь - с тестовым набором.

Имеет дело со всем, что вы ему бросаете, включая NULL, 0, 0.000 или отрицательные числа.

Но у меня есть сомнения по поводу вашего пункта 3 в комментариях.

...