【问题标题】:Calculating modulus for large numbers in PL/SQL在 PL/SQL 中计算大数的模数
【发布时间】:2014-01-03 16:21:30
【问题描述】:

我正在尝试在 PL/SQL 中计算大模数。我实现了一个我在这个位置找到的方法:http://en.wikipedia.org/wiki/International_Bank_Account_Number 在 IBAN 上的模运算下。但是当我使用这个数字时,我得到了错误的结果: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. 重复步骤 2-3,直到处理完 D 的所有数字

步骤 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。对于大多数数字,计算器是正确的。

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

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;

【问题讨论】:

    标签: plsql modulus


    【解决方案1】:

    使用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 数据类型的精度约为 38 位,因此对于非常大的数字,将四舍五入,因此您的输出以 0000 结尾,而不是您输入的数字.

    【讨论】:

      猜你喜欢
      • 2011-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多