Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/dart/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2012/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Plsql 优雅的PL/SQL比较两个小数位数不同的数字_Plsql_Numbers_Precision - Fatal编程技术网

Plsql 优雅的PL/SQL比较两个小数位数不同的数字

Plsql 优雅的PL/SQL比较两个小数位数不同的数字,plsql,numbers,precision,Plsql,Numbers,Precision,我需要一个函数来比较两个数字,以便10.12432=10.124和10.12432!=10.123。也就是说,更准确的数字需要根据不准确数字的小数位数进行四舍五入 以下函数似乎满足了我的需要(根据Noel的评论进行了编辑): 但是,imho并不是最令人满意的方法。是否有更好的解决方案,特别是不使用字符串的解决方案?如果只是为了知道小数点后有多少位,您总是需要稍微使用字符串。使用最小字符串的一种解决方案是: FUNCTION eq(number_1 in number, number_2 in n

我需要一个函数来比较两个数字,以便
10.12432=10.124
10.12432!=10.123
。也就是说,更准确的数字需要根据不准确数字的小数位数进行四舍五入

以下函数似乎满足了我的需要(根据Noel的评论进行了编辑):


但是,imho并不是最令人满意的方法。是否有更好的解决方案,特别是不使用字符串的解决方案?

如果只是为了知道小数点后有多少位,您总是需要稍微使用字符串。使用最小字符串的一种解决方案是:

FUNCTION eq(number_1 in number, number_2 in number) return boolean is
    dot1 NUMBER;
    dot2 NUMBER;
    min_places  NUMBER;
BEGIN
    dot1 := INSTR( number_1, '.' );
    dot2 := INSTR( number_2, '.' );
    IF( dot1 = 0 OR dot2 = 0 )
    THEN
        min_places := 0;
    ELSE
        min_places := NVL( LEAST( LENGTH( SUBSTR(number_1, dot1+1) )
                                , LENGTH( SUBSTR(number_2, dot2+1) )
                                ), 0 );
    END IF;
    RETURN ROUND( number_1, min_places ) = ROUND( number_2, min_places );
END eq;
编辑:刚刚从示例中了解了要舍入的第二个参数。我以前使用的是
*电源(10个,最小位置)
。谢谢。

试试这个:

CREATE OR REPLACE function are_equal(i_num1 number, i_num2 number) return number deterministic is
  l_rv number := 0;
  l_places number := 0;
begin
  l_places := least(length(trim(regexp_replace(i_num1, '[^.]+\.(.*)$', '\1'))),length(trim(regexp_replace(i_num2, '[^.]+\.(.*)$', '\1'))));
  l_rv := case when (round(i_num1,l_places) = round(i_num2,l_places))  then 1 
  else 0 end;
  return l_rv;
end;

请注意,我正在舍入(根据您的帖子),并返回1或0,而不是布尔值(在pl/sql之外的上下文中更有用)。

好的,我想我找到了一个不需要字符串转换的解决方案。开枪

declare
  function find_precision(
    p_input in number
  ) return number
  is
    l_check number;
  begin
    for l_i in 0 .. 39
    loop
      l_check := round(p_input, l_i);
      -- as soon as l_check is the same number as p_input we have reached the right number of decimals
      if l_check = p_input
      then
        return l_i;
      end if;
    end loop;
    -- should never get here
    raise value_error;
  end find_precision;

  function lossy_compare(
    p_number1 in number
  , p_number2 in number
  ) return boolean
  is
    l_precision number;
  begin
    l_precision := least(find_precision(p_number1), find_precision(p_number2));
    return round(p_number1, l_precision) = round(p_number2, l_precision);
  end lossy_compare;
begin

  if lossy_compare(10.12432, 10.124)
  then
    dbms_output.put_line('equal');
  else
    dbms_output.put_line('not equal');
  end if;

  if lossy_compare(10.12432, 10.123)
  then
    dbms_output.put_line('equal');
  else
    dbms_output.put_line('not equal');
  end if;
end;
/
请注意,此代码认为
10.124000
的精度为3(实际上是6),但这与解决问题无关

编辑 重新考虑我的最后一句话,这可能是不正确的。当比较10.124000和10.124001时,您希望得到什么结果?我的解决方案会给出“相等”(因为它本质上是将10.124与10.124001进行比较),而有人可能会说“不相等”(因为尾随的0确实增加了精度)。

我意识到这是一篇很老的帖子,但我最近看到了几个关于浮点值的问题。因此,当我遇到这个问题时,我找到了一个我用来进行浮点比较的老程序。希望这对未来的搜索者有用。顺便说一句,它不需要转换为字符串,因为它比较相对值

create or replace function compare_float(
                            float_value_1  double precision
                          , float_value_2  double precision
                          , delta_value    double precision default 1.0e-6
                          )
 return integer
 is
 /* 
    Name:          Compare_Float
    Author:        Belayer
    Date Written:  22-Jan-2009

    Floating point number are ALWAYS estimates as there is no way to precisely a
    decimal base 10 number in binary. Therefore comparing 2 floating points of 
    for equality is a risky proposition at best. 

    While the above is true, we can at least inquire as to the difference being
    enough as to not matter and then consider the values as equal even though
    in the computed since they may not be.

    This routine implements the 'close enough' comparison to give the relative 
    magnitude relationship between 2 floating point variables.

    Parameters:
      Float_Value_1. The first of 2 floating point values to be compared. 
      Float_Value_2. The second of he 2 floating point values to be compared.
      Delta_Value.   The amount of the 2 values can differ and still be considered 
                     to be equal.  Default value 10^-6 (.000001) This value can be
                     overridden on each call to the procedure.
    Return Values:
      -1 Float_Value_1 is less than Float_Value_2.
       0 Float_Value_1 equals Float_Value_2 within specified Datla_Value.
       1 Float_Value_1 is greater than Float_Value_2.

*/ 
     delta_diff           double precision;
     compare_float_result integer;
begin
    delta_diff := float_value_1 - float_value_2;
    if abs(delta_diff) < delta_value
    then
        compare_float_result := 0;
    else 
        compare_float_result := sign(delta_diff);
    end if;    
    return compare_float_result;
end compare_float; 

是的,有人想要它。注意:此例程源于更旧的Fortran

对于10.012432和10,此功能失败。0124@Noel你说得对。谢谢你指出这一点。正则表达式是错误的。我已将
FM
添加到数字格式,并更改了正则表达式。它现在似乎可以工作了。我将使用
LENGTH(number_1-TRUNC(number_1))-1
来计算小数的数量,而不是正则表达式。
create or replace function compare_float(
                            float_value_1  double precision
                          , float_value_2  double precision
                          , delta_value    double precision default 1.0e-6
                          )
 return integer
 is
 /* 
    Name:          Compare_Float
    Author:        Belayer
    Date Written:  22-Jan-2009

    Floating point number are ALWAYS estimates as there is no way to precisely a
    decimal base 10 number in binary. Therefore comparing 2 floating points of 
    for equality is a risky proposition at best. 

    While the above is true, we can at least inquire as to the difference being
    enough as to not matter and then consider the values as equal even though
    in the computed since they may not be.

    This routine implements the 'close enough' comparison to give the relative 
    magnitude relationship between 2 floating point variables.

    Parameters:
      Float_Value_1. The first of 2 floating point values to be compared. 
      Float_Value_2. The second of he 2 floating point values to be compared.
      Delta_Value.   The amount of the 2 values can differ and still be considered 
                     to be equal.  Default value 10^-6 (.000001) This value can be
                     overridden on each call to the procedure.
    Return Values:
      -1 Float_Value_1 is less than Float_Value_2.
       0 Float_Value_1 equals Float_Value_2 within specified Datla_Value.
       1 Float_Value_1 is greater than Float_Value_2.

*/ 
     delta_diff           double precision;
     compare_float_result integer;
begin
    delta_diff := float_value_1 - float_value_2;
    if abs(delta_diff) < delta_value
    then
        compare_float_result := 0;
    else 
        compare_float_result := sign(delta_diff);
    end if;    
    return compare_float_result;
end compare_float; 
-- test 1.3e-6 (default)
with vals (f,s,e) as 
     ( select 10.12432,  10.124,      1  from dual union all
       select 10.124,    10.124001,  -1  from dual union all
       select 1.0000124,  1.0000120,  0  from dual union all
       select 1.000124,   1.000120,   1  from dual union all       
       select 1.11000015, 1.12000015,-1  from dual union all
       select 0.0000010,  0.00000011, 0  from dual
     ) 
select compare_float(f,s) result, e expecting
  from vals;
-- test 1.3e-3 (default)  
with vals (f,s,e) as 
     ( select 10.12432,  10.124,      0  from dual union all
       select 10.124,    10.124001,   0  from dual union all
       select 1.0000124,  1.0000120,  0  from dual union all
       select 1.000124,   1.000120,   0  from dual union all       
       select 1.11000015, 1.12000015,-1  from dual union all
       select 0.0000010,  0.00000011, 0  from dual
     ) 
select compare_float(f,s, 1.0e-3) result, e expecting
  from vals; '
-- test 1.3e-7  
with vals (f,s,e) as 
     ( select 10.12432,  10.124,      1  from dual union all
       select 10.124,    10.124001,  -1  from dual union all
       select 1.0000124,  1.0000120,  1  from dual union all
       select 1.000124,   1.000120,   1  from dual union all       
       select 1.11000015, 1.12000015,-1  from dual union all
       select 0.0000010,  0.00000011,-1  from dual
     ) 
select compare_float(f,s, 1.0e-7) result, e expecting
  from vals;