3

10.12432 = 10.124とのように 2 つの数値を比較する関数が必要10.12432 != 10.123です。つまり、精度の低い数値の小数点以下の桁数に応じて丸める必要がある、より正確な数値です。

次の関数は、私が必要とすることを行うようです(ノエルのコメントに従って編集):

function eq(number_1 in number, number_2 in number) return boolean is
    string_1 varchar2(100); 
    string_2 varchar2(100); 

    num_1    number;
    num_2    number;

    len      number;
begin
    string_1 := to_char(number_1, 'FM99999999999999999999999999999.99999999999999999999999999999');
    string_2 := to_char(number_2, 'FM99999999999999999999999999999.99999999999999999999999999999');

    string_1 := regexp_replace(string_1, '.*(\..*)', '\1');
    string_2 := regexp_replace(string_2, '.*(\..*)', '\1');

    len   := least(length(string_1), length(string_2)) - 1;

    num_1 := round(number_1, len);
    num_2 := round(number_2, len);

    return num_1 = num_2;

end eq;

しかし、私見ですが、これは最も満足のいくアプローチではありません。より良い解決策、特に文字列に頼らない解決策はありますか?

4

4 に答える 4

0

OK、文字列変換なしの解決策を見つけたと思います。シュート!

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 が精度を追加するため)。

于 2013-06-05T10:34:18.123 に答える
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; 

以下は、delta_value に指定された異なる値を使用して同じ値をテストします。値間の許容差は依然として等しいと見なされます。値 1.0e-6 (デフォルト)、1.0e-3、および 1.0e-7 でテストを実行します。

-- 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;  

まぁ欲しい人はいるだろうけど。注: このルーチンは、はるかに古い Fortran をソースとしています。

于 2020-02-17T07:39:16.863 に答える
0

小数点以下の桁数を知るためだけに、常に文字列を少し使用する必要があります。最小限の文字列を使用する 1 つのソリューションは次のとおりです。

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;

編集: 例から丸める 2 番目のパラメーターについて学びました。以前使用して*POWER(10,min_places)いました。ありがとう。

于 2013-05-30T08:00:53.383 に答える