由于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/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/4227/viewspace-432940/