发表于: 2008.08.30 23:50
分类: ORACLE
出处: http://yangtingkun.itpub.net/post/468/469892
---------------------------------------------------------------
由于Oracle的数值类型的最大精度只有38位,因此对于高精度的数值计算就需要使用其他的方法来实现。
这篇文章利用字符串来保存高精度数值,并实现了两个字符串中数值的运算。
这篇给出基础的字符串除法的解决方案。
利用字符串实现高精度数值运算(一):http://yangtingkun.itpub.net/post/468/469206
利用字符串实现高精度数值运算(二):http://yangtingkun.itpub.net/post/468/469241
利用字符串实现高精度数值运算(三):http://yangtingkun.itpub.net/post/468/469423
利用字符串实现高精度数值运算(四):http://yangtingkun.itpub.net/post/468/469550
由于除法相对其他算法而言,比较复杂,因此将除法的计算分拆到多篇文章中进行描述。
这里实现一个最简单的除法运算,无论是除数还是被除数都没有超过Oracle的NUMBER类型的精度范围。
SQL> CREATE OR REPLACE FUNCTION F_DIVITE_STR(P_DIVITED IN VARCHAR2, P_DIVITING IN VARCHAR2) RETURN V
ARCHAR2 AS
2 V_RESULT VARCHAR2(32767) := SUBSTR(TO_NUMBER(P_DIVITED) / TO_NUMBER(P_DIVITING), 1, 37);
3 V_COUNT NUMBER := 1;
4 P_PERCISION NUMBER DEFAULT 101;
5 V_LENGTH NUMBER := -1;
6 BEGIN
7
8 LOOP
9 DBMS_OUTPUT.PUT_LINE(V_RESULT);
10 EXIT WHEN LENGTH(V_RESULT) > P_PERCISION
11 OR V_LENGTH = CASE WHEN INSTR(V_RESULT, '.') > 0 THEN LENGTH(RTRIM(RTRIM(V_RESULT, '0'), '.'
)) ELSE LENGTH(V_RESULT) END;
12 V_LENGTH := LENGTH(V_RESULT);
13 V_COUNT := V_COUNT + 1;
14
15 V_RESULT := V_RESULT ||
16 REPLACE(SUBSTR(TO_NUMBER(F_STR_MULTI(F_STR_SUB(P_DIVITED, F_STR_MULTI(V_RESULT, P_DIVITING))
, RPAD(1, 37 * V_COUNT, '0')))
17 / TO_NUMBER(P_DIVITING), 1, 37), '.', '');
18
19 END LOOP;
20 RETURN V_RESULT;
21 END;
22 /
函数已创建。
这里首先计算最简单的被除数和除数都可以通过NUMBER类型精度表示的数值相除。不过由于除法的特殊性,这里引入了另一个参数,来指定除法保留的小数位数。
除法的计算没有再使用加法、减法和乘法程序使用的递归算法,而是采用了循环算法。当小数位数满足需要,或者余数为0时,退出循环,返回最终结果。
简单的测试一下:
SQL> SELECT F_DIVITE_STR('100', '3') FROM DUAL;
F_DIVITE_STR('100','3')
-------------------------------------------------------------------------------------------
33.3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
SQL> SELECT F_DIVITE_STR('1', '7') FROM DUAL;
F_DIVITE_STR('1','7')
-------------------------------------------------------------------------------------------
.142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142857142
SQL> SELECT F_DIVITE_STR('0.2', '0.011') FROM DUAL;
F_DIVITE_STR('0.2','0.011')
-------------------------------------------------------------------------------------------
18.1818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181818181











