yangtingkun
===========================================================
利用字符串实现高精度数值运算(二)
===========================================================

由于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

yangtingkun 发表于:2008.08.22 21:11 ::分类: ( ORACLE ) ::阅读:(12573次) :: 评论 (5)
re: 利用字符串实现高精度数值运算(二) [回复]

发现一点瑕疵

--------------------------
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);
--------------------------

应改为
V_RESULT1 := SUBSTR(V_RESULT1, 1, LENGTH(V_RESULT1) - V_LENGTH_OTHER_2) || '.' ||
lpad(V_RESULT1, length(V_OTHER_STR2), '0');
V_RESULT2 := SUBSTR(V_RESULT2, 1, LENGTH(V_RESULT2) - V_LENGTH_OTHER_1) || '.' ||
lpad(V_RESULT2, length(V_OTHER_STR1), '0');

foolcatjr 评论于: 2008.09.16 12:42
re: 利用字符串实现高精度数值运算(二) [回复]

对不起,我写的也没考虑周全,一会重发

foolcatjr 评论于: 2008.09.16 12:52
re: 利用字符串实现高精度数值运算(二) [回复]

现更正如下

if V_LENGTH_OTHER_2 > length(V_INTEGER_STR1) then
V_RESULT1 := '0.' || lpad(V_RESULT1, length(V_OTHER_STR2), '0');
else
V_RESULT1 := SUBSTR(V_RESULT1,
1,
LENGTH(V_RESULT1) - V_LENGTH_OTHER_2) || '.' ||
SUBSTR(V_RESULT1, -V_LENGTH_OTHER_2);
end if;
if V_LENGTH_OTHER_1 > length(V_INTEGER_STR2) then
V_RESULT2 := '0.' || lpad(V_RESULT2, length(V_OTHER_STR1), '0');
else
V_RESULT2 := SUBSTR(V_RESULT2,
1,
LENGTH(V_RESULT2) - V_LENGTH_OTHER_1) || '.' ||
SUBSTR(V_RESULT2, -V_LENGTH_OTHER_1);
end if;

foolcatjr 评论于: 2008.09.16 13:06
re: 利用字符串实现高精度数值运算(二) [回复]

之所以加上小数位的判定是如果其中一个数的小数位大于另一个数的整数位时,结果应该全部是小数部分。

V_RESULT1 := SUBSTR(V_RESULT1, 1, LENGTH(V_RESULT1) - V_LENGTH_OTHER_2)
|| '.'
|| SUBSTR(V_RESULT1, - V_LENGTH_OTHER_2);
其中 substr(val,-n),若n>length(val),则返回null,而不是整个串,所以原写法会将这一部分结果忽略掉。

本来是在家写好的,不知咋地,没存上,所以发了两次,见谅。

ps:怎么排好版的到这上面来格式全丢了?忽略了tab键位?

foolcatjr 评论于: 2008.09.16 13:14
re: 利用字符串实现高精度数值运算(二) [回复]

嗯,有问题是正常的。
这个系列还在不断的调整测试之中。

现在手头事情比较多,等忙完这阵会将这部分补齐

yangtingkun 评论于: 2008.09.16 15:04

发表评论
标题

在此添加评论
表情符号: smile laughing tongue angry crying sad wassat wink

称呼

邮箱地址(可选)

个人主页(可选)

 authimage


切换风格
新闻聚合
博客日历
文章归档...
最新发表...
最新评论...
最多阅读文章...
最多评论文章...
博客统计...
Blog信息
网站链接...