发表于: 2008.08.22 21:11
分类: ORACLE
出处: http://yangtingkun.itpub.net/post/468/469241
---------------------------------------------------------------
由于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











