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

Posted

技术标签:

【中文标题】返回给定数字的 N 个有效数字的函数【英文标题】:Function to return N significant digits of given number 【发布时间】:2018-11-06 22:34:59 【问题描述】:

我看过几个关于这个主题的问题,但答案对我来说似乎没有用。

我必须在 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

这三个结果是不正确的,因为:

    小数点后的零不是有效数字。 所有非零数字都有效。 十进制中除零以外的数字后的零很重要。

根据第 3 点,上一个示例的正确结果是:0.080 而不是 0.08,虽然从数学上证明是相同的,但从视觉上我必须得到这个结果。关于如何解决我的问题的一些想法?我想返回VARCHAR 以换取NUMERIC 是要提出的解决方案的一部分。

一些想法或我错过了一些东西。谢谢。

【问题讨论】:

你确定你是意思第3点吗?因为小数点后的尾随零对数值无关紧要... 那么你有答案了吗? 【参考方案1】:

这将实现您的要求:

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;

dbfiddle here - 带有测试用例。

处理您扔给它的所有内容,包括NULL00.000 或负数。

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

【讨论】:

【参考方案2】:

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)

【讨论】:

以上是关于返回给定数字的 N 个有效数字的函数的主要内容,如果未能解决你的问题,请参考以下文章

2022-08-06:给定一个数组arr,长度为N,arr中所有的值都在1~K范围上, 你可以删除数字,目的是让arr的最长递增子序列长度小于K。 返回至少删除几个数字能达到目的。 N <= 10^4

2022-06-16:给定一个数组arr,含有n个数字,都是非负数, 给定一个正数k, 返回所有子序列中,累加和最小的前k个子序列累加和。 假设K不大,怎么算最快? 来自亚马逊。

以逗号分隔的字符串形式返回一个数字

ZZNUOJ_C语言1101:逆序数字(函数专题)(完整代码)

对于仅包含数字的给定字符串,返回所有有效IP地址组合的最佳方法是什么?

回旋数字矩阵