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

yangtingkun發表於2008-08-22

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

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

這篇描述兩個字串相乘。

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

 

 

上一篇給出了字串表示的數值相加的函式,這一篇繼續描述字串表示數值相乘的演算法。

採用程式碼重用的方法,利用以前處理整數乘法的基礎,加上小數部分的處理。整數部分演算法描述可以參考:http://yangtingkun.itpub.net/post/468/241044

由於包含了小數部分,一個乘法變成4個乘法的相加,因此還要利用前面一篇文章的字串相加的函式:

SQL> CREATE OR REPLACE FUNCTION F_STR_MULTI(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_MULTI_STR(P_MUL1 IN VARCHAR2, P_MUL2 IN VARCHAR2) RETURN VARCHAR2 AS
 21    V_LENGTH1 NUMBER DEFAULT LENGTH(P_MUL1);
 22    V_LENGTH2 NUMBER DEFAULT LENGTH(P_MUL2);
 23   BEGIN
 24    IF V_LENGTH1 > 19 THEN
 25     RETURN F_STR_ADD(F_MULTI_STR(SUBSTR(P_MUL1, 1, V_LENGTH1 - 19), P_MUL2) || LPAD('0', 19, '0'),
 26     F_MULTI_STR(SUBSTR(P_MUL1, V_LENGTH1 - 18), P_MUL2));
 27    ELSIF V_LENGTH2 > 19 THEN
 28     RETURN F_STR_ADD(F_MULTI_STR(P_MUL1, SUBSTR(P_MUL2, 1, V_LENGTH2 - 19)) || LPAD('0', 19, '0'),
 29     F_MULTI_STR(P_MUL1, SUBSTR(P_MUL2, V_LENGTH2 - 18)));
 30    ELSE
 31     RETURN TO_NUMBER(P_MUL1) * TO_NUMBER(P_MUL2);
 32    END IF;
 33   END;
 34  
 35  BEGIN
 36   V_RESULT1 := F_MULTI_STR(V_INTEGER_STR1, V_OTHER_STR2);
 37   V_RESULT2 := F_MULTI_STR(V_INTEGER_STR2, V_OTHER_STR1);
 38   V_RESULT1 := SUBSTR(V_RESULT1, 1, LENGTH(V_RESULT1) - V_LENGTH_OTHER_2)
 39    || '.'
 40    || SUBSTR(V_RESULT1, - V_LENGTH_OTHER_2);
 41   V_RESULT2 := SUBSTR(V_RESULT2, 1, LENGTH(V_RESULT2) - V_LENGTH_OTHER_1)
 42    || '.'
 43    || SUBSTR(V_RESULT2, - V_LENGTH_OTHER_1);
 44   RETURN F_STR_ADD
 45    (
 46     F_STR_ADD
 47      (
 48       F_MULTI_STR(V_INTEGER_STR1, V_INTEGER_STR2)
 49        || '.'
 50        || LPAD(
 51         F_MULTI_STR(V_OTHER_STR1, V_OTHER_STR2),
 52         V_LENGTH_OTHER_1 + V_LENGTH_OTHER_2,
 53         '0'),
 54       V_RESULT1
 55      ),
 56     V_RESULT2
 57    );
 58  END;
 59  /

函式已建立。

下面簡單測試一下函式的功能:

SQL> SELECT F_STR_MULTI('12345678900987654321', '555') FROM DUAL;

F_STR_MULTI('12345678900987654321','555')
------------------------------------------------------------------------
6851851790048148148155

SQL> SELECT F_STR_MULTI('1.2345678900987654321', '555') FROM DUAL;

F_STR_MULTI('1.2345678900987654321','555')
------------------------------------------------------------------------
685.1851790048148148155

SQL> SELECT F_STR_MULTI('0.12345678900987654321', '0.00555') FROM DUAL;

F_STR_MULTI('0.12345678900987654321','0.00555')
------------------------------------------------------------------------
.0006851851790048148148155

SQL> SELECT F_STR_MULTI('6.125', '4.8') FROM DUAL;

F_STR_MULTI('6.125','4.8')
------------------------------------------------------------------------
29.4

SQL> SELECT 12345678900987654321*555 FROM DUAL;

12345678900987654321*555
------------------------
              6.8519E+21

 

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

相關文章