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

yangtingkun發表於2008-08-26

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

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

這篇給出完整的字串相減的解決方法。

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

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

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

 

 

上面一篇已經介紹瞭如何實現整數的字串相減,有了這個作為基礎,只需要分別對整數和小數部分分別處理,並將結果規整返回即可:

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

106    RPAD(V_OTHER_STR2, GREATEST(V_LENGTH_OTHER_1, V_LENGTH_OTHER_2), '0'));
107  
108   IF SUBSTR(V_RESULT1, 1, 1) = '-' THEN
109    IF SUBSTR(V_RESULT2, 1, 1) = '-' OR LTRIM(V_RESULT2, '0') IS NULL THEN
110     RETURN '-' || NVL(LTRIM(RTRIM(RTRIM(SUBSTR(V_RESULT1, 2) || '.' || SUBSTR(V_RESULT2, 2), '0'), '.'), '0'), '0');
111    ELSE
112     RETURN '-' || NVL(LTRIM(RTRIM(RTRIM(F_SUB_STR(SUBSTR(V_RESULT1, 2), '1') || '.'
113      || F_STR_ADD(F_SUB_STR(LPAD('9', LENGTH(V_RESULT2), '9'), V_RESULT2), '1')
114      , '0'), '.'), '0'), '0');
115    END IF;
116   ELSIF LTRIM(V_RESULT1, '0') IS NULL THEN
117    RETURN NVL(RTRIM('.' || RTRIM(V_RESULT2, '0'), '.'), 0);
118   ELSE
119    IF SUBSTR(V_RESULT2, 1, 1) = '-' THEN
120     RETURN NVL(LTRIM(RTRIM(RTRIM(F_SUB_STR(V_RESULT1, '1') || '.'
121      || F_STR_ADD(F_SUB_STR(LPAD('9', LENGTH(V_RESULT2), '9'), SUBSTR(V_RESULT2, 2)), '1')
122      , '0'), '.'), '0'), '0');
123    ELSE
124     RETURN NVL(LTRIM(RTRIM(RTRIM(V_RESULT1 || '.' || V_RESULT2, '0'), '.'), '0'), '0');
125    END IF;
126   END IF;
127  END;
128  /

函式已建立。

透過幾個簡單的例子驗證一下結果:

SQL> SELECT F_STR_SUB('123.1293482734982379482374',
  2  '812384.123412834991238234823794822342') FROM DUAL;

F_STR_SUB('123.1293482734982379482374','812384.123412834991238234823794822342')
--------------------------------------------------------------------------------
-812260.994064561493000286586394822342

SQL> SELECT F_STR_SUB('812384.123412834991238234823794822342',
  2  '123.1293482734982379482374') FROM DUAL;

F_STR_SUB('812384.123412834991238234823794822342','123.1293482734982379482374')
--------------------------------------------------------------------------------
812260.9994064561493000286586394822342

SQL> SELECT F_STR_SUB('812384.1293482734982379482374',
  2  '123.123412834991238234823794822342') FROM DUAL;

F_STR_SUB('812384.1293482734982379482374','123.123412834991238234823794822342')
--------------------------------------------------------------------------------
812261.005935438506999713413605177658

SQL> SELECT F_STR_SUB('123.123412834991238234823794822342',
  2  '812384.1293482734982379482374') FROM DUAL;

F_STR_SUB('123.123412834991238234823794822342','812384.1293482734982379482374')
--------------------------------------------------------------------------------
-812261.005935438506999713413605177658

 

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

相關文章