利用字串實現高精度數值運算(三)

yangtingkun發表於2008-08-25

由於Oracle的數值型別的最大精度只有38位,因此對於高精度的數值計算就需要使用其他的方法來實現。

這篇文章利用字串來儲存高精度數值,並實現了兩個字串中數值的運算。

這篇描述兩個表示整數的字串相減。

利用字串實現高精度數值運算(一):http://yangtingkun.itpub.net/post/468/469206

利用字串實現高精度數值運算(二):http://yangtingkun.itpub.net/post/468/469241

 

 

上一篇給出了字串相乘的演算法,這一篇繼續探討減法的實現。由於前兩篇文章實際上都使用了以前實現的整數部分的程式碼,而這一篇則沒有相應的程式碼可以重用。

因此模仿前面的實現,將這個複雜的問題拆分成兩個相對簡單一些的問題,首先實現整數部分的減法,然後再擴充套件到小數部分。

SQL> CREATE OR REPLACE FUNCTION F_ADD_STR(P_ADD1 IN VARCHAR2, P_ADD2 IN VARCHAR2) RETURN VARCHAR2 AS

  2   V_LENGTH1 NUMBER DEFAULT LENGTH(P_ADD1);
  3   V_LENGTH2 NUMBER DEFAULT LENGTH(P_ADD2);
  4  BEGIN
  5   IF V_LENGTH1 > 37 THEN
  6    RETURN
  7     F_ADD_STR
  8     (
  9      SUBSTR(P_ADD1, 1, V_LENGTH1 - 37),
 10      NVL
 11      (
 12       SUBSTR
 13       (
 14        F_ADD_STR(SUBSTR(P_ADD1, V_LENGTH1 - 36), P_ADD2),
 15        1,
 16        LENGTH(F_ADD_STR(SUBSTR(P_ADD1, V_LENGTH1 - 36), P_ADD2)) - 37
 17       ),
 18       '0'
 19      )
 20     ) || SUBSTR(F_ADD_STR(SUBSTR(P_ADD1, V_LENGTH1 - 36), P_ADD2), - 37);
 21   ELSIF V_LENGTH2 > 37 THEN
 22    RETURN
 23     F_ADD_STR
 24     (
 25      NVL
 26      (
 27       SUBSTR
 28       (
 29        F_ADD_STR(P_ADD1, SUBSTR(P_ADD2, V_LENGTH2 - 36)),
 30        1,
 31        LENGTH(F_ADD_STR(P_ADD1, SUBSTR(P_ADD2, V_LENGTH2 - 36))) - 37
 32       ),
 33       '0'
 34      ),
 35      SUBSTR(P_ADD2, 1, V_LENGTH2 - 37)
 36     )
 37     || SUBSTR(F_ADD_STR(P_ADD1, SUBSTR(P_ADD2, V_LENGTH2 - 36)), - 37);
 38   ELSE
 39    RETURN
 40     LTRIM
 41     (
 42      TO_CHAR
 43      (
 44       TO_NUMBER(P_ADD1) + TO_NUMBER(P_ADD2),
 45       RPAD
 46       (
 47        '0',
 48        GREATEST(V_LENGTH1, V_LENGTH2, LENGTH(TO_NUMBER(P_ADD1) + TO_NUMBER(P_ADD2))),
 49        '9'
 50       )
 51      )
 52     );
 53   END IF;
 54  END;
 55  /

函式已建立。

先建立F_STR_ADD函式,這個函式需要被F_SUB_STR呼叫,將其作為F_SUB_STR的內部函式沒有必要,而且會明顯增加程式碼的長度。

SQL> CREATE OR REPLACE FUNCTION F_SUB_STR(P_SUB1 IN VARCHAR2, P_SUB2 IN VARCHAR2) RETURN VARCHAR2 AS
  2    V_LENGTH1 NUMBER DEFAULT LENGTH(P_SUB1);
  3    V_LENGTH2 NUMBER DEFAULT LENGTH(P_SUB2);
  4    V_RES1 VARCHAR2(32767);
  5    V_RES2 VARCHAR2(32767);
  6  BEGIN
  7   IF SUBSTR(P_SUB1, 1, 1) = '-' THEN
  8    IF SUBSTR(P_SUB2, 1, 1) = '-' THEN
  9     RETURN F_SUB_STR(SUBSTR(P_SUB2, 2), SUBSTR(P_SUB1, 2));
 10    ELSE
 11     RETURN '-' || F_ADD_STR(SUBSTR(P_SUB1, 2), P_SUB2);
 12    END IF;
 13   ELSE
 14    IF SUBSTR(P_SUB2, 1, 1) = '-' THEN
 15     RETURN F_ADD_STR(SUBSTR(P_SUB1, 2), P_SUB2);
 16    END IF;
 17   END IF;
 18   IF V_LENGTH1 > 37 AND V_LENGTH2 > 37 THEN
 19    V_RES1 := F_SUB_STR(SUBSTR(P_SUB1, 1, V_LENGTH1 - 37), SUBSTR(P_SUB2, 1, V_LENGTH2 - 37));
 20    V_RES2 := F_SUB_STR(SUBSTR(P_SUB1, V_LENGTH1 - 36), SUBSTR(P_SUB2, V_LENGTH2 - 36));
 21    IF SUBSTR(V_RES1, 1, 1) = '-' THEN
 22     IF SUBSTR(V_RES2, 1, 1) = '-' OR LTRIM(V_RES2, '0') IS NULL THEN
 23      RETURN V_RES1 || SUBSTR(V_RES2, 2);
 24     ELSE
 25      RETURN '-' || F_SUB_STR(SUBSTR(V_RES1, 2), '1')
 26       || F_ADD_STR(F_SUB_STR(LPAD('9', 37, '9'), V_RES2), LPAD('1', 37, '0'));
 27     END IF;
 28    ELSIF LTRIM(V_RES1, '0') IS NULL THEN
 29     RETURN V_RES2;
 30    ELSE
 31     IF SUBSTR(V_RES2, 1, 1) = '-' THEN
 32      RETURN F_SUB_STR(V_RES1, 1)
 33       || F_ADD_STR(F_SUB_STR(LPAD('9', 37, '9'), SUBSTR(V_RES2, 2)), LPAD('1', 37, '0'));
 34     ELSE
 35      RETURN V_RES1 || V_RES2;
 36     END IF;
 37    END IF;
 38   ELSIF V_LENGTH2 > 37 THEN
 39    V_RES1 := F_SUB_STR('0', SUBSTR(P_SUB2, 1, V_LENGTH2 - 37));
 40    V_RES2 := F_SUB_STR(P_SUB1, SUBSTR(P_SUB2, V_LENGTH2 - 36));  
 41    IF SUBSTR(V_RES1, 1, 1) = '-' THEN
 42     IF SUBSTR(V_RES2, 1, 1) = '-' OR LTRIM(V_RES2, '0') IS NULL  THEN
 43      RETURN V_RES1 || SUBSTR(V_RES2, 2);
 44     ELSE
 45      RETURN '-' || F_SUB_STR(SUBSTR(V_RES1, 2), '1')
 46       || F_ADD_STR(F_SUB_STR(LPAD('9', 37, '9'), V_RES2), LPAD('1', 37, '0'));
 47     END IF;
 48    ELSIF LTRIM(V_RES1, '0') IS NULL THEN
 49     RETURN V_RES2;
 50    ELSE
 51     IF SUBSTR(V_RES2, 1, 1) = '-' THEN
 52      RETURN F_SUB_STR(V_RES1, 1)
 53       || F_ADD_STR(F_SUB_STR(LPAD('9', 37, '9'), SUBSTR(V_RES2, 2)), LPAD('1', 37, '0'));
 54     ELSE
 55      RETURN V_RES1 || V_RES2;
 56     END IF;
 57    END IF;
 58   ELSIF V_LENGTH1 > 37 THEN
 59    V_RES1 := SUBSTR(P_SUB1, 1, V_LENGTH1 - 37);
 60    V_RES2 := F_SUB_STR(SUBSTR(P_SUB1, V_LENGTH1 - 36), P_SUB2);
 61    IF SUBSTR(V_RES1, 1, 1) = '-' THEN
 62     IF SUBSTR(V_RES2, 1, 1) = '-' OR LTRIM(V_RES2, '0') IS NULL THEN
 63      RETURN V_RES1 || SUBSTR(V_RES2, 2);
 64     ELSE
 65      RETURN '-' || F_SUB_STR(SUBSTR(V_RES1, 2), '1')
 66       || F_ADD_STR(F_SUB_STR(LPAD('9', 37, '9'), V_RES2), LPAD('1', 37, '0'));
 67     END IF;
 68    ELSIF LTRIM(V_RES1, '0') IS NULL THEN
 69     RETURN V_RES2;
 70    ELSE
 71     IF SUBSTR(V_RES2, 1, 1) = '-' THEN
 72      RETURN F_SUB_STR(V_RES1, 1)
 73       || F_ADD_STR(F_SUB_STR(LPAD('9', 37, '9'), SUBSTR(V_RES2, 2)), LPAD('1', 37, '0'));
 74     ELSE
 75      RETURN V_RES1 || V_RES2;
 76     END IF;
 77    END IF;
 78   ELSE
 79    RETURN SUBSTR(SIGN(TO_NUMBER(P_SUB1) - TO_NUMBER(P_SUB2)), -2, 1)
 80     || LPAD(ABS(TO_NUMBER(P_SUB1) - TO_NUMBER(P_SUB2)), GREATEST(V_LENGTH1, V_LENGTH2), '0');
 81   END IF;
 82  END;
 83  /

函式已建立。

下面驗證一下:

SQL> SELECT F_SUB_STR('123451234512345', '5432154321') FROM DUAL;

F_SUB_STR('123451234512345','5432154321')
--------------------------------------------------------------------------------
123445802358024

SQL> SELECT F_SUB_STR('5432154321', '123451234512345') FROM DUAL;

F_SUB_STR('5432154321','123451234512345')
--------------------------------------------------------------------------------
-123445802358024

SQL> SELECT F_SUB_STR('55555555555555555555555555555555555555', '2222222222222') FROM DUAL;

F_SUB_STR('55555555555555555555555555555555555555','2222222222222')
--------------------------------------------------------------------------------
55555555555555555555555553333333333333

SQL> SELECT F_SUB_STR('55555555555555555555555555555555555555',
  2  '2222222222222222222222222222222222222222222222222') FROM DUAL;

F_SUB_STR('55555555555555555555555555555555555555','2222222222222222222222222222
--------------------------------------------------------------------------------
-2222222222166666666666666666666666666666666666667

SQL> SELECT F_SUB_STR('55555555555555555555555555555555555555',
  2  '66666666666666666666666666666666666666') FROM DUAL;

F_SUB_STR('55555555555555555555555555555555555555','6666666666666666666666666666
--------------------------------------------------------------------------------
-11111111111111111111111111111111111111

SQL> SELECT F_SUB_STR('555555555555555555555555555555555555555',
  2  '66666666666666666666666666666666666666') FROM DUAL;

F_SUB_STR('555555555555555555555555555555555555555','666666666666666666666666666
--------------------------------------------------------------------------------
488888888888888888888888888888888888889

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4227/viewspace-434627/,如需轉載,請註明出處,否則將追究法律責任。

相關文章