最低分数值在Oracle中

时间:2018-02-19 11:44:07

标签: oracle function math stored-procedures lcm

我正在尝试创建一个返回最低分数值的函数。示例代码在这里:

create or replace function fraction_sh(x number) return varchar2
is
    fra1 number;
    pwr number;
    intprt number;
    v4 number;
    numer number;
    denom number;
    gcdval number;
    frac varchar2(50);
    begin
       if x <> 0 then  
           fra1 := mod(x,1);
           pwr := length(mod(x,1))-1;
           intprt := trunc(x);
           numer :=mod(x,1)*power(10,length(mod(x,1))-1);
           denom :=power(10,length(mod(x,1))-1);
           gcdval := gcdnew(power(10,length(mod(x,1))-1),mod(x,1)*power(10,length(mod(x,1))-1));
               if intprt = 0 then 
                       frac := to_char(trunc(numer/gcdval))||'/'||to_char(trunc(denom/gcdval));
                DBMS_OUTPUT.put_line(1||' '||denom||' '||gcdval||' '||numer);
              else
                    frac := (intprt*to_char(trunc(denom/gcdval)))+to_char(trunc(numer/gcdval))||'/'||to_char(trunc(denom/gcdval));
                   DBMS_OUTPUT.put_line(2||' '||denom||' '||gcdval||' '||numer);
              end if;
        end if;
     return frac;
end;


create or replace function gcdnew (a number, b number, p_precision number default null, orig_larger_num number default null) return number is
v_orig_larger_num number := greatest(nvl(orig_larger_num,-1),a,b);
v_precision_level number := p_precision;
begin
  if a is null or b is null or (a = 0 and b = 0) then return 1; end if;

  if p_precision is null or p_precision <= 0 then
      v_precision_level := 4; 
  end if;

  if b is null or b = 0 or (b/v_orig_larger_num <= power(10,-1*v_precision_level) and greatest(a,b) <> v_orig_larger_num) then
      return a;
  else
      return (gcdnew(b,mod(a,b),v_precision_level,v_orig_larger_num));
  end if;

端;

在大多数情况下它起作用,但是当我尝试通过2/11时它返回2/10。

任何帮助表示感谢。

2 个答案:

答案 0 :(得分:0)

你可以像这样使用:

create or replace function fraction_sh(dividing number,divided number) return varchar2
is 
dividing2 number;
divided2 number;
frac varchar2(100 char); 
temp number;
loop_value boolean;
    begin
     loop_value:=true;
     dividing2:=dividing;
     divided2 :=divided;
       if dividing <> 0 then  
           while loop_value
           loop
           if gcd(dividing2,divided2)<> 1 then
           temp:=gcd(dividing2,divided2);
            dividing2:=dividing2/temp;
            divided2 :=divided2/temp;
            frac:=dividing2||'/'||divided2; 
            else
            loop_value:=false;
            frac:=dividing2||'/'||divided2; 
           end if;          
           end loop;
        else
        frac:='0';
        end if;
     return frac;
end;

gcd func:

create or replace function gcd(a number, b number)
  return number is
  begin
     if b = 0 then
        return a;
     else
        return gcd(b,mod(a,b));
     end if;
  end;

答案 1 :(得分:0)

您目前正在做的事情的问题是精确度。对于2/11,结果数字为0.1818181 ...重复出现,并且其长度 - 因此pwr值 - 最终为40,这会破坏以后的计算。

通过修改来限制精度(并且稍微整理一下,主要是为了在已经有方便的变量时删除重复计算):

create or replace function fraction_sh(p_float number) return varchar2
is
  l_precision pls_integer := 10;
  l_int_part pls_integer;
  l_frac_part number;
  l_power pls_integer;
  l_numer number;
  l_denom number;
  l_gcdval number;
  l_result varchar2(99);
begin
  if p_float is null or p_float = 0 then
    return null;
  end if;

  l_int_part := trunc(p_float);
  l_frac_part := round(mod(p_float, 1), l_precision);
  l_power := length(l_frac_part);
  l_denom := power(10, l_power);
  l_numer := l_frac_part * l_denom;

  l_gcdval := gcdnew(l_denom, l_numer, ceil(l_precision/2));

  if l_int_part = 0 then 
    l_result := trunc(l_numer/l_gcdval) ||'/'|| trunc(l_denom/l_gcdval);
  else
    l_result := l_int_part * (trunc(l_denom/l_gcdval) + trunc(l_numer/l_gcdval))
      ||'/'|| trunc(l_denom/l_gcdval);
  end if;

  return l_result;
end;
/

获得:

with t(n) as (
            select 9/12 from dual
  union all select 2/11 from dual
  union all select 1/2 from dual
  union all select 1/3 from dual
  union all select 1/4 from dual
  union all select 1/5 from dual
  union all select 1/6 from dual
  union all select 1/7 from dual
  union all select 1/8 from dual
  union all select 1/9 from dual
  union all select 1/10 from dual
  union all select 4/3 from dual
  union all select 0 from dual
  union all select 1 from dual
)
select n, fraction_sh(n) as fraction
from t;

        N FRACTION                      
---------- ------------------------------
       .75 3/4                           
.181818182 2/11                          
        .5 1/2                           
.333333333 1/3                           
       .25 1/4                           
        .2 1/5                           
.166666667 1/6                           
.142857143 1/7                           
      .125 1/8                           
.111111111 1/9                           
        .1 1/10                          
1.33333333 4/3                           
         0                               
         1 1/1                           

所以你可能想要为传入1添加一些处理,或者在舍入结束后为1/1添加近似值 - 可能只是为了在任何一种情况下返回普通'1'

我已将l_precision设置为10而非任意,你可以将其设置得更大,但会在某些时候遇到问题,所以请仔细测试你选择的任何值。

(我根本没看过gdcnew;这可能会有点简化。)