计算PL / SQL中大数的模数

时间:2014-01-03 16:21:30

标签: plsql modulus

我正在尝试计算PL / SQL中的大模数。我实现了一个我在这个位置找到的方法:在IBAN上的Modulo操作下http://en.wikipedia.org/wiki/International_Bank_Account_Number。但是当我使用这个号码时,我得到错误的结果:221021290110000123452229211210282900128222984。

这就是这个方法需要完成的方法:

分段计算D mod 97可以通过多种方式完成。其中一种方法如下:

  1. 从D的最左边数字开始,使用前9位数字构建一个数字,并将其称为N。
  2. 计算N mod 97.如果结果小于10,则在结果前面加上0,结果范围为00到96.
  3. 通过将上面的结果(步骤2)与D的下一个7位数连接起来构造一个新的9位数N.如果D中剩余的数字少于7位但至少有一位,那么构造一个新的N,它将具有小于9位,从上面的结果(步骤2)开始,接着是D
  4. 的剩余数字
  5. 重复步骤2-3,直到处理完D的所有数字
  6. 步骤2中的最终计算结果为D mod 97 = N mod 97。

    我做了一些日志记录,这些是所有模数步骤的结果:

    221021290110000123452229211210282900128222984 (221021290 % 97 = 0)
    00110000123452229211210282900128000000 (001100001 % 97 = 21)
    2123452229211210282900128000000 (212345222 & 97 = 0)
    009211210282900128000000 (009211210 % 97 = 90)
    90282900128000000 (902829001 % 97 = 46)
    4628000000 (462800000 % 97 = 2)
    020 = 20
    

    结果需要是1.我用模数计算器检查它,它需要是1.大多数数字的calcutor是正确的。

    这是我制作的代码,这个数字怎么可能不正确:

    create or replace function doubleDiget (iban number)
    return varchar2
    as
    begin
      if(iban <= 9) then
        return concat('0',iban);
      else
        return iban;
      end if;
    end doubleDiget;
    
    create or replace FUNCTION modbig (iban number)   
        RETURN varchar2                     
          AS  
          lengthIban number(38);
          modUitkomts number(38);
          modUitkomtsc varchar(38);
          restIban varchar(38);
          modlength number(38);
      BEGIN
    
      modUitkomts := SUBSTR(iban,0,9) mod 97;
      modUitkomtsc := doubleDiget(modUitkomts);
      restIban := concat(modUitkomtsc,SUBSTR(iban,10)); 
      dbms_output.put_line(restIban);
    
      loop
        if( length(restIban) >= 9) then
            modUitkomts := SUBSTR(restIban,0,9) mod 97;
            modUitkomtsc := doubleDiget(modUitkomts);
            restIban := concat(modUitkomtsc,SUBSTR(restIban,10)); 
            dbms_output.put_line(restIban);
        else
          exit;
        end if;
      end loop;
    
      modUitkomts := restIban mod 97;
      return modUitkomts;
    
    
    END modbig;
    
    begin
    DBMS_OUTPUT.PUT_LINE(modbig(221021290110000123452229211210282900128222984));  
    end;
    

1 个答案:

答案 0 :(得分:4)

使用VARCHAR2代替NUMBER

SQL> DECLARE
  2     FUNCTION modbig(iban VARCHAR2) RETURN VARCHAR2 IS -- won't be rounded
  3        lengthIban   NUMBER(38);
  4        modUitkomts  NUMBER(38);
  5        modUitkomtsc VARCHAR(38);
  6        restIban     VARCHAR(50);
  7        modlength    NUMBER(38);
  8     BEGIN
  9        restIban     := iban;
 10        modUitkomts  := SUBSTR(restIban, 0, 9) MOD 97;
 11        modUitkomtsc := LPAD(modUitkomts, 2, '0');
 12        restIban := concat(modUitkomtsc, SUBSTR(iban, 10));
 13  
 14        LOOP
 15           IF (length(restIban) >= 9) THEN
 16              modUitkomts  := SUBSTR(restIban, 0, 9) MOD 97;
 17              modUitkomtsc := LPAD(modUitkomts, 2, '0');
 18              restIban := concat(modUitkomtsc, SUBSTR(restIban, 10));
 19           ELSE
 20              EXIT;
 21           END IF;
 22        END LOOP;
 23        modUitkomts := restIban MOD 97;
 24        RETURN modUitkomts;
 25     END modbig;
 26  
 27  BEGIN
 28     DBMS_OUTPUT.PUT_LINE(
 29        modbig('221021290110000123452229211210282900128222984')); -- varchar2
 30  END;
 31  /
1
PL/SQL procedure successfully completed

说明:NUMBER dataype的精度约为38位,因此对于非常大的数字,此将被舍入,因此您的输出以0000结尾,而不是你输入的数字。