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

yangtingkun發表於2008-08-21

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

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

這篇描述兩個字串相加。

 

 

其實在以前處理超大數值的時候,寫過一個字串相加的函式,不過當時這個函式只是處理正整數的相加,沒有考慮小數的情況。詳細描述可以參考:http://yangtingkun.itpub.net/post/468/241044

當前面臨的問題則主要是小數精度的問題。不過這並不影響對原有程式碼的重用,只需要在原有的程式碼外面巢狀一層,分別對整數部分和小數部分進行運算,並最終將結果合併起來即可。

SQL> CREATE OR REPLACE FUNCTION F_STR_ADD (P_STR1 IN VARCHAR2, P_STR2 IN VARCHAR2) RETURN VARCHAR2 AS
  2   V_INTEGER_STR1 VARCHAR2(32767) := NVL(
  3    SUBSTR(P_STR1, 1,
  4     CASE INSTR(P_STR1, '.') WHEN 0 THEN LENGTH(P_STR1) ELSE INSTR(P_STR1, '.') - 1 END
  5    ), 0);
  6   V_INTEGER_STR2 VARCHAR2(32767) := NVL(
  7    SUBSTR(P_STR2, 1,
  8     CASE INSTR(P_STR2, '.') WHEN 0 THEN LENGTH(P_STR2) ELSE INSTR(P_STR2, '.') - 1 END
  9    ), 0);
 10   V_OTHER_STR1 VARCHAR2(32767) := CASE INSTR(P_STR1, '.')
 11    WHEN 0 THEN NULL ELSE SUBSTR(P_STR1, INSTR(P_STR1, '.') + 1) END;
 12   V_OTHER_STR2 VARCHAR2(32767) := CASE INSTR(P_STR2, '.')
 13    WHEN 0 THEN NULL ELSE SUBSTR(P_STR2, INSTR(P_STR2, '.') + 1) END;
 14   V_LENGTH_OTHER_1 NUMBER := NVL(LENGTH(V_OTHER_STR1), 0);
 15   V_LENGTH_OTHER_2 NUMBER := NVL(LENGTH(V_OTHER_STR2), 0);
 16   V_RESULT VARCHAR2(32767);
 17 
 18   FUNCTION F_ADD_STR(P_ADD1 IN VARCHAR2, P_ADD2 IN VARCHAR2) RETURN VARCHAR2 AS
 19    V_LENGTH1 NUMBER DEFAULT LENGTH(P_ADD1);
 20    V_LENGTH2 NUMBER DEFAULT LENGTH(P_ADD2);
 21   BEGIN
 22    IF V_LENGTH1 > 37 THEN
 23     RETURN
 24      F_ADD_STR
 25      (
 26       SUBSTR(P_ADD1, 1, V_LENGTH1 - 37),
 27       NVL
 28       (
 29        SUBSTR
 30        (
 31         F_ADD_STR(SUBSTR(P_ADD1, V_LENGTH1 - 36), P_ADD2),
 32         1,
 33         LENGTH(F_ADD_STR(SUBSTR(P_ADD1, V_LENGTH1 - 36), P_ADD2)) - 37
 34        ),
 35        '0'
 36       )
 37      ) || SUBSTR(F_ADD_STR(SUBSTR(P_ADD1, V_LENGTH1 - 36), P_ADD2), - 37);
 38    ELSIF V_LENGTH2 > 37 THEN
 39     RETURN
 40      F_ADD_STR
 41      (
 42       NVL
 43       (
 44        SUBSTR
 45        (
 46         F_ADD_STR(P_ADD1, SUBSTR(P_ADD2, V_LENGTH2 - 36)),
 47         1,
 48         LENGTH(F_ADD_STR(P_ADD1, SUBSTR(P_ADD2, V_LENGTH2 - 36))) - 37
 49        ),
 50        '0'
 51       ),
 52       SUBSTR(P_ADD2, 1, V_LENGTH2 - 37)
 53      )
 54      || SUBSTR(F_ADD_STR(P_ADD1, SUBSTR(P_ADD2, V_LENGTH2 - 36)), - 37);
 55    ELSE
 56     RETURN
 57      LTRIM
 58      (
 59       TO_CHAR
 60       (
 61        TO_NUMBER(P_ADD1) + TO_NUMBER(P_ADD2),
 62        RPAD
 63        (
 64         '0',
 65         GREATEST(V_LENGTH1, V_LENGTH2, LENGTH(TO_NUMBER(P_ADD1) + TO_NUMBER(P_ADD2))),
 66         '9'
 67        )
 68       )
 69      );
 70    END IF;
 71   END;
 72 
 73  BEGIN
 74   IF V_LENGTH_OTHER_1 >= V_LENGTH_OTHER_2 THEN
 75    V_RESULT := F_ADD_STR
 76     (V_OTHER_STR1,
 77     V_OTHER_STR2 || LPAD('0', V_LENGTH_OTHER_1 - V_LENGTH_OTHER_2, '0'));
 78   ELSE
 79    V_RESULT := F_ADD_STR
 80     (V_OTHER_STR1 || LPAD('0', V_LENGTH_OTHER_2 - V_LENGTH_OTHER_1, '0'),
 81     V_OTHER_STR2);
 82   END IF;
 83  
 84   IF LENGTH(V_RESULT) > GREATEST(V_LENGTH_OTHER_1, V_LENGTH_OTHER_2) THEN
 85    RETURN NVL(LTRIM(RTRIM(
 86      F_ADD_STR
 87       (F_ADD_STR(V_INTEGER_STR1, V_INTEGER_STR2), 1)
 88       || '.' || RTRIM(SUBSTR(V_RESULT, 2), '0'), 
 89      '.'), '0'), '0');
 90   ELSE
 91    RETURN NVL(LTRIM(RTRIM(
 92     F_ADD_STR(V_INTEGER_STR1, V_INTEGER_STR2)
 93      || '.' || RTRIM(V_RESULT, '0'),
 94     '.'), '0'), '0');
 95   END IF;
 96  END;
 97  /

函式已建立。

程式碼比較長,這裡就不過多的描述實現了,下面簡單看一下利用這個函式進行計算:

SQL> SELECT F_STR_ADD('12345.6789', '98.7654321') FROM DUAL;

F_STR_ADD('12345.6789','98.7654321')
------------------------------------------------------------------------------------------
12444.4443321

SQL> SELECT F_STR_ADD('5555', '12939') FROM DUAL;

F_STR_ADD('5555','12939')
------------------------------------------------------------------------------------------
18494

SQL> SELECT F_STR_ADD('0.234', '74') FROM DUAL;

F_STR_ADD('0.234','74')
------------------------------------------------------------------------------------------
74.234

SQL> SELECT F_STR_ADD('0.00001111111111111111111111111111111111111', '.234111111111111') FROM DUAL;

F_STR_ADD('0.00001111111111111111111111111111111111111','.234111111111111')
---------------------------------------------------------------------------------------
.23412222222222211111111111111111111111111

 

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

相關文章