ITPub博客

首页 > Linux操作系统 > Linux操作系统 > 数值超过NUMBER最大表示范围的问题[转]

数值超过NUMBER最大表示范围的问题[转]

原创 Linux操作系统 作者:bartfj 时间:2008-10-30 10:02:23 0 删除 编辑

昨天同事给我出了一道简单的题——计算1阶乘到100阶乘的累加。问题似乎很简单,但是最终引出的问题并不简单.

刚开始觉得问题很简单,写了一个简单的函数:

SQL> CREATE OR REPLACE FUNCTION F_SUM_MULTI(P_IN IN NUMBER) RETURN NUMBER AS
2 V_RESULT_MULTI NUMBER DEFAULT 1;
3 V_RESULT NUMBER DEFAULT 0;
4 BEGIN
5 FOR I IN 1..P_IN LOOP
6 V_RESULT_MULTI := V_RESULT_MULTI * I;
7 V_RESULT := V_RESULT + V_RESULT_MULTI;
8 END LOOP;
9 RETURN V_RESULT;
10 END;
11 /

函数已创建。

SQL> SELECT F_SUM_MULTI(5) FROM DUAL;

F_SUM_MULTI(5)
--------------
153

代码很简单,功能也已经实现了。但是,问题并不想我想的这么简单。

SQL> SELECT F_SUM_MULTI(100) FROM DUAL;

F_SUM_MULTI(100)
----------------
~

奇怪,输出结果为什么会是~呢?莫非是超过了NUMBER能表示的最大的范围?

SQL> SELECT F_SUM_MULTI(83) FROM DUAL;

F_SUM_MULTI(83)
---------------
3.994E+124

SQL> SELECT F_SUM_MULTI(84) FROM DUAL;

F_SUM_MULTI(84)
---------------
~

果然是超过了NUMBER能表示的最大的范围。NUMBER类型能表达的最大值是9.9999999999999999999999999999999999*10E125。以前还从没有碰到过超出Oracle最大处理范围的情况,也一直没有想到过会碰到超出最大精度。看来阶乘不愧是结果增长最迅速的操作。

由于超过了Oracle能处理的最大值,Oracle已经很难处理这个问题了。莫非已经没有办法来处理这个问题了?

 

 

上文以及提到,由于计算的数值的大小以及超过了Oracle所能表示的最大范围,因此,Oracle中已经无法进行计算了。

首先考虑的是能否通过外部过程来实现。利用CJAVA程序来计算,并将最终结果通过字符串的方式返回给Oracle

这篇文章给出通过外部C过程的方式来实现。本例是模仿TomEXPERT ONE ON ONE ORACLEC外部过程例子进行编写的。OCI程序的连接、初始化,以及数据传递部分和Tom的例子很相似,只是在需要的地方进行了一些小的修改。

整个程序代码如下,multi_sum.c

#include
#include
#include
#include
#include
#include
#include

#include

#define INI_FILE_NAME "/tmp/multi_sum.ini"

typedef struct ociStruct
{
OCIExtProcContext * ctx;
OCIEnv * envhp;
OCISvcCtx * svchp;
OCIError * errhp;

ub1 debugf_flag;
char debugf_path[255];
char debugf_filename[50];
} ocihStruct;

void _debugf(ocihStruct * ocih, char * fmt, ...)
{
va_list ap;
OCIFileObject * fp;
time_t theTime=time(NULL);
char msg[8192];
ub4 bytes;

if (OCIFileOpen(ocih->envhp, ocih->errhp, &fp, ocih->debugf_filename, ocih->debugf_path,
OCI_FILE_WRITE_ONLY, OCI_FILE_APPEND|OCI_FILE_CREATE, OCI_FILE_TEXT) != OCI_SUCCESS)
return;
strftime(msg, sizeof(msg), "%Y%m%d%H%M%S GMT", gmtime(&theTime));
OCIFileWrite(ocih->envhp, ocih->errhp, fp, msg, strlen(msg), &bytes);

va_start(ap, fmt);
vsprintf(msg, fmt, ap);
va_end(ap);
strcat(msg, "n");

OCIFileWrite(ocih->envhp, ocih->errhp, fp, msg, strlen(msg), &bytes);
OCIFileClose(ocih->envhp, ocih->errhp, fp);
}

void _debugf(ocihStruct * ocih, char * fmt, ...);

#define debugf if((ocih!=NULL) && (ocih->debugf_flag)) _debugf

static int raise_application_error(ocihStruct * ocih, int errCode, char * errMsg, ...)
{
char msg[8192];
va_list ap;

va_start(ap, errMsg);
vsprintf(msg, errMsg, ap);
va_end(ap);

debugf(ocih, "raise application error(%d, %s)", errCode, msg);
if(OCIExtProcRaiseExcpWithMsg(ocih->ctx, errCode, msg, 0) == OCIEXTPROC_ERROR)
{
debugf(ocih, "Unable to raise exception");
}
return -1;
}

static char * lastOciError(ocihStruct * ocih)
{
sb4 errcode;
char * errbuf=(char *)OCIExtProcAllocCallMemory(ocih->ctx, 256);

strcpy(errbuf, "unable to retrieve messagen");
OCIErrorGet(ocih->errhp, 1, NULL, &errcode, errbuf, 255, OCI_HTYPE_ERROR);
errbuf[strlen(errbuf) - 1] = 0;
return errbuf;
}

static ocihStruct * init(OCIExtProcContext * ctx)
{
ub1 false = 0;
ocihStruct *ocih = NULL;
OCIEnv *envhp;
OCISvcCtx *svchp;
OCIError *errhp;
ub4 key = 1;

if (OCIExtProcGetEnv(ctx, &envhp, &svchp, &errhp) != OCI_SUCCESS)
{
OCIExtProcRaiseExcpWithMsg(ctx, 20000, "failed to get OCI Connection", 0);
return NULL;
}

if (OCIContextGetValue(envhp, errhp, (ub1 *)&key, sizeof(key), (dvoid **)&ocih) != OCI_SUCCESS)
{
OCIExtProcRaiseExcpWithMsg(ctx, 20000, "failed to get OCI Context", 0);
return NULL;
}

if (ocih == NULL)
{
if (OCIMemoryAlloc(envhp, errhp, (dvoid **)&ocih, OCI_DURATION_PROCESS, sizeof(ocihStruct),
OCI_MEMORY_CLEARED) != OCI_SUCCESS)
{
OCIExtProcRaiseExcpWithMsg(ctx, 20000, "failed to get OCI Memory", 0);
return NULL;
}
ocih->ctx = ctx;
ocih->envhp = envhp;
ocih->svchp= svchp;
ocih->errhp= errhp;
if (OCIContextSetValue(envhp, errhp, OCI_DURATION_SESSION, (ub1 *)&key, sizeof(key), ocih) != OCI_SUCCESS)
{
raise_application_error(ocih, 20000, "%s", lastOciError(ocih));
return NULL;
}

if ((OCIExtractInit(envhp, errhp) != OCI_SUCCESS) ||
(OCIExtractSetNumKeys(envhp, errhp, 3) != OCI_SUCCESS) ||
(OCIExtractSetKey(envhp, errhp, "debugf", OCI_EXTRACT_TYPE_BOOLEAN, 0, &false, NULL, NULL) != OCI_SUCCESS) ||
(OCIExtractSetKey(envhp, errhp, "debugf_filename", OCI_EXTRACT_TYPE_STRING, 0, "extproc.log", NULL, NULL)
!= OCI_SUCCESS) ||
(OCIExtractSetKey(envhp, errhp, "debugf_path", OCI_EXTRACT_TYPE_STRING, 0, "", NULL, NULL) != OCI_SUCCESS) ||
(OCIExtractFromFile(envhp, errhp, 0, INI_FILE_NAME) != OCI_SUCCESS) ||
(OCIExtractToBool(envhp, errhp, "debugf", 0, &ocih->debugf_flag) != OCI_SUCCESS) ||
(OCIExtractToStr(envhp, errhp, "debugf_filename", 0, ocih->debugf_filename, sizeof(ocih->debugf_filename))
!= OCI_SUCCESS) ||
(OCIExtractToStr(envhp, errhp, "debugf_path", 0, ocih->debugf_path, sizeof(ocih->debugf_path))
!= OCI_SUCCESS) ||
(OCIExtractTerm(envhp, errhp) != OCI_SUCCESS))
{
raise_application_error(ocih, 20000, "%s", lastOciError(ocih));
return NULL;
}
}
else
{
ocih->ctx = ctx;
ocih->envhp =envhp;
ocih->svchp = svchp;
ocih->errhp = errhp;
}

if (OCIFileInit(ocih->envhp, ocih->errhp) != OCI_SUCCESS)
{
raise_application_error(ocih, 20000, "%s", lastOciError(ocih));
return NULL;
}
return ocih;
}

static void term(ocihStruct * ocih)
{
OCIFileTerm(ocih->envhp, ocih->errhp);
}

#define ERROR_OCI_ERROR 20001
#define ERROR_STR_TOO_SMALL 20002


char * multi_sum (OCIExtProcContext * ctx, int p_number, short p_inumber_i, short * return_i, int * return_l)
{
char * return_value;
ocihStruct * ocih;

double result=0, result_mul=1;
int n=100, i;
char res[4000];

if ((ocih=init(ctx)) == NULL)
return NULL;

if (p_inumber_i == OCI_IND_NOTNULL)
{
n = p_number;
}

for(i=1;i<=n;i++)
{
result_mul*=i;
result+=result_mul;
}
sprintf(res, "%lf", result);
debugf(ocih, "Enter return String");
return_value = (char *)OCIExtProcAllocCallMemory(ctx, strlen(res) + 1);
if (return_value == NULL)
{
raise_application_error(ocih, ERROR_OCI_ERROR, "%s", "no memory");
}
else
{
*return_i = OCI_IND_NULL;
strcpy(return_value, res);
*return_l = strlen(return_value);
*return_i = OCI_IND_NOTNULL;
}

term(ocih);
return return_value;
}

然后,利用MAKE文件,将其编译成.so文件,makefile文件内容如下:

MAKEFILE= $(ORACLE_HOME)/rdbms/demo/demo_rdbms.mk

INCLUDE= -I$(ORACLE_HOME)/rdbms/demo
-I$(ORACLE_HOME)/rdbms/public
-I$(O
RACLE_HOME)/plsql/public
-I$(ORACLE_HOME)/network/public

TGTDLL= multi_sum.so
OBJS= multi_sum.o

all:$(TGTDLL)

clean:
rm *.o *.so

$(TGTDLL):$(OBJS)
$(MAKE) -f $(MAKEFILE) extproc_callback
SHARED_LIBNAME=$(TGTDLL) BJS=$(OBJS)

CC=cc
CFLAGS= -g -I. $(INCLUDE) -Wall

通过make生成.so文件:

[oracle@member oracle]$ make
cc -g -I. -I/opt/ora9/product/9.2/rdbms/demo -I/opt/ora9/product/9.2/rdbms/public -I/opt/ora9/product/9.2/plsql/public -I/opt/ora9/product/9.2/network/public -Wall -c -o multi_sum.o multi_sum.c
make -f /opt/ora9/product/9.2/rdbms/demo/demo_rdbms.mk extproc_callback
SHARED_LIBNAME=multi_sum.so BJS=multi_sum.o
make[1]: Entering directory `/home/oracle'
/usr/bin/ld -shared -L/opt/ora9/product/9.2/lib -o multi_sum.so multi_sum.o /opt/ora9/product/9.2/lib/libpls9.a -lclntsh `cat /opt/ora9/product/9.2/lib/sysliblist` -ldl -lm
make[1]: Leaving directory `/home/oracle'
[oracle@member oracle]$ cp multi_sum.so /opt/ora9/product/9.2/lib/

然后创建LIBRARY和函数:

SQL> CREATE OR REPLACE LIBRARY MULTI_SUM_C AS '/opt/ora9/product/9.2/lib/multi_sum.so'
2 /

Library created.

SQL> CREATE OR REPLACE FUNCTION F_MULTI_SUM_C(P_IN IN BINARY_INTEGER) RETURN VARCHAR2 AS
2 LANGUAGE C NAME "multi_sum" LIBRARY MULTI_SUM_C WITH CONTEXT
3 PARAMETERS (
4 CONTEXT,
5 P_IN INT,
6 P_IN INDICATOR SHORT,
7 RETURN INDICATOR SHORT,
8 RETURN LENGTH INT,
9 RETURN STRING
10 );
11 /

Function created.

在执行过程之前,首先要保证/tmp目录下有一个multi_sum.ini文件,这个文件是用来跟踪、调试OCI程序的。文件内容类似于:

[oracle@member oracle]$ cd /tmp
[oracle@member tmp]$ ls multi_sum.ini
multi_sum.ini
[oracle@member tmp]$ more multi_sum.ini
debugf=false
debugf_filename=multi_sum.log
debugf_path=/tmp/

下面就可以通过SQL进行调用了:

SQL> SELECT F_MULTI_SUM_C(5) FROM DUAL;

F_MULTI_SUM_C(5)
------------------------------------------------------------------------------
153.000000

SQL> SELECT F_MULTI_SUM_C(100) FROM DUAL;

F_MULTI_SUM_C(100)
------------------------------------------------------------------------------
94269001683709925420719656762546985486727416355083585338645224398394941811201963496134808913580530755456740099840414271964178383003109887091841526063934996480.000000

外部过程尤其是C的外部过程建立比较复杂,这里就不详细描述了。

 

 

上文以及给出了用C外部过程的解决方案,但是使用C来解决这个问题,实在是很麻烦,需要编写OCI程序,利用MAKE文件编译成.so文件,还需要配置监听程序等一系列的复杂操作。

和上面这些额外的复杂操作形成鲜明对比的是,真正在C中进行计算的核心代码实际上只有几行,因此这里选择C外部过程感觉有点得不偿失。

那么使用JAVA外部过程是否会简单一点呢:

SQL> create or replace and compile java source named "multi_sum" as
2 import java.math.BigDecimal;
3 public class multi_sum extends Object
4 {
5 public static String multi_sum_java (int n)
6 {
7 BigDecimal result = new BigDecimal(0);
8 BigDecimal result_mul = new BigDecimal(1);
9 for(int i=1;i<=n;i++)
10 {
11 result_mul = result_mul.multiply(new BigDecimal(i));
12 result = result.add(result_mul);
13 }
14 return result.unscaledValue().toString();
15 }
16 }
17 /

Java created.

SQL> create or replace function f_multi_sum_java(p_in in number) return varchar2 as
2 language java name 'multi_sum.multi_sum_java(int) return String';
3 /

Function created.

SQL> SELECT F_MULTI_SUM_JAVA(5) FROM DUAL;

F_MULTI_SUM_JAVA(5)
------------------------------------------------------------------
153

SQL> SELECT F_MULTI_SUM_JAVA(100) FROM DUAL;

F_MULTI_SUM_JAVA(100)
------------------------------------------------------------------
94269001683709979260859834124473539872070722613982672442938359305624678223479506023400294093599136466986609124347432647622826870038220556442336528920420940313

C外部过程不同,JAVA可用直接在数据库中编译、装载、运行,因此,JAVA外部过程显得比C外部过程要简单得多。

最后值得一提是,利用JAVA过程的BigDecimal对象,可以得到一个确切的数值。而在c中使用的是标准数据类型double,使用double类型只能保证前16位有效数字,后面的数值都是不准确的。

我的Java水平很低,因此自己实现的时候采用的也是double类型,这个程序是同事帮忙完善的,利用javadouble类型,仍然只能得到16位有效数字。

 

 

上面几篇文章演示了使用Oracle的外部过程,来解决数值超过NUMBER最大范围的问题,那么在Oracle中,是否就没有办法解决了?

确实,在Oracle中使用NUMBER类型是没有办法表示超过10126的数值的。虽然NUMBER无法表示,但是可以利用字符串来表示。

不过,为了能计算超过NUMBER范围和精度的数值,需要写过程来实现两个字符串的“加”和“乘”。

下面展示了两个过程,思想都是一个,将字符串的加、乘转化为NUMBER精度范围以内的加和乘,然后通过字符串操作将结果合并起来。

SQL> CREATE OR REPLACE FUNCTION F_ADD_STR(P_ADD1 IN VARCHAR2, P_ADD2 IN VARCHAR2) RETURN VARCHAR2 AS
2 V_LENGTH1 NUMBER DEFAULT LENGTH(P_ADD1);
3 V_LENGTH2 NUMBER DEFAULT LENGTH(P_ADD2);
4 BEGIN
5 IF V_LENGTH1 > 37 THEN
6 RETURN
7 F_ADD_STR
8 (
9 SUBSTR(P_ADD1, 1, V_LENGTH1 - 37),
10 NVL
11 (
12 SUBSTR
13 (
14 F_ADD_STR(SUBSTR(P_ADD1, V_LENGTH1 - 36), P_ADD2),
15 1,
16 LENGTH(F_ADD_STR(SUBSTR(P_ADD1, V_LENGTH1 - 36), P_ADD2)) - 37
17 ),
18 '0'
19 )
20 )
21 || SUBSTR(F_ADD_STR(SUBSTR(P_ADD1, V_LENGTH1 - 36), P_ADD2), - 37);
22 ELSIF V_LENGTH2 > 37 THEN
23 RETURN
24 F_ADD_STR
25 (
26 NVL
27 (
28 SUBSTR
29 (
30 F_ADD_STR(P_ADD1, SUBSTR(P_ADD2, V_LENGTH2 - 36)),
31 1,
32 LENGTH(F_ADD_STR(P_ADD1, SUBSTR(P_ADD2, V_LENGTH2 - 36))) - 37
33 ),
34 '0'
35 ),
36 SUBSTR(P_ADD2, 1, V_LENGTH2 - 37)
37 )
38 || SUBSTR(F_ADD_STR(P_ADD1, SUBSTR(P_ADD2, V_LENGTH2 - 36)), - 37);
39 ELSE
40 RETURN
41 LTRIM
42 (
43 TO_CHAR
44 (
45 TO_NUMBER(P_ADD1) + TO_NUMBER(P_ADD2),
46 RPAD
47 (
48 '0',
49 GREATEST(V_LENGTH1, V_LENGTH2, LENGTH(TO_NUMBER(P_ADD1) + TO_NUMBER(P_ADD2))),
50 '9'
51 )
52 )
53 );
54 END IF;
55 END;
56 /

函数已创建。

SQL> CREATE OR REPLACE FUNCTION F_MULTI_STR(P_MUL1 IN VARCHAR2, P_MUL2 IN VARCHAR2) RETURN VARCHAR2
AS
2 V_LENGTH1 NUMBER DEFAULT LENGTH(P_MUL1);
3 V_LENGTH2 NUMBER DEFAULT LENGTH(P_MUL2);
4 BEGIN
5 IF V_LENGTH1 > 19 THEN
6 RETURN F_ADD_STR(F_MULTI_STR(SUBSTR(P_MUL1, 1, V_LENGTH1 - 19), P_MUL2) || LPAD('0', 19, '0'),
7 F_MULTI_STR(SUBSTR(P_MUL1, V_LENGTH1 - 18), P_MUL2));
8 ELSIF V_LENGTH2 > 19 THEN
9 RETURN F_ADD_STR(F_MULTI_STR(P_MUL1, SUBSTR(P_MUL2, 1, V_LENGTH2 - 19)) || LPAD('0', 19, '0'),
10 F_MULTI_STR(P_MUL1, SUBSTR(P_MUL2, V_LENGTH2 - 18)));
11 ELSE
12 RETURN TO_NUMBER(P_MUL1) * TO_NUMBER(P_MUL2);
13 END IF;
14 END;
15 /

函数已创建。

过程的详细算法这里就不描述了。有了字符串的“加”和“乘”的过程,写一个计算阶乘的过程就很容易了。

SQL> CREATE OR REPLACE FUNCTION F_MULTI_SUM_PLSQL(P_IN IN NUMBER) RETURN VARCHAR2 IS
2 V_RESULT_MULTI VARCHAR2(32767) DEFAULT '1';
3 V_RESULT VARCHAR2(32767) DEFAULT '0';
4 BEGIN
5 FOR I IN 1..P_IN LOOP
6 V_RESULT_MULTI := F_MULTI_STR(V_RESULT_MULTI, I);
7 V_RESULT := F_ADD_STR(V_RESULT, V_RESULT_MULTI);
8 END LOOP;
9 RETURN V_RESULT;
10 END;
11 /

函数已创建。

SQL> SELECT F_MULTI_SUM_PLSQL(5) FROM DUAL;

F_MULTI_SUM_PLSQL(5)
------------------------------------------------------------------
153

SQL> SELECT F_MULTI_SUM_PLSQL(100) FROM DUAL;

F_MULTI_SUM_PLSQL(100)
------------------------------------------------------------------
94269001683709979260859834124473539872070722613982672442938359305624678223479506023400294093599136466986609124347432647622826870038220556442336528920420940313

这个方法的好处是,理论上可以处理任何长度的数字。如果一个数字的长度超过了字符串的最大长度32K1032767次方,基本上很难碰到这么大的数字),还可以选择CLOB来替代VARCHAR2,那么能够处理的数值应该足够了。

这个方法还有一个好处是不会损失任何的精度,当然,对于一个很大的数而言,往往不需要这么高的精度。

这个过程的最大问题是效率问题。本来PL/SQL就比CJAVA要慢,采用了这种复杂的算法,处理速度就更慢了。不过处理上面这个问题,需要0.2秒左右,还不是很明显

 

上面一篇文章给出了利用字符串的“加”和“乘”的方法,在ORACLEPL/SQL中解决这个问题。其中,由于要保证精度,加法和乘法都对输入值进行限制,将计算范围缩短到ORACLENUMBER精度范围之内,通过函数的多次递归来得到最终结果。

不过,两个函数相对比较复杂,可读性较差,于是这里给出一个利用循环算法的解决方案,并利用Oracle的对象功能,将数据和方法封装起来。

SQL> CREATE OR REPLACE TYPE T_BIG_NUMBER IS OBJECT
2 (
3 BIG_NUMBER VARCHAR2(32767),
4 NUMBER_LENGTH NUMBER,
5 STATIC FUNCTION F_T_BIG_NUMBER (P_BIG_NUMBER IN VARCHAR2) RETURN T_BIG_NUMBER,
6 MEMBER FUNCTION ADDS(P_ADD IN T_BIG_NUMBER) RETURN T_BIG_NUMBER,
7 MEMBER FUNCTION MULTIS(P_MULTI IN T_BIG_NUMBER) RETURN T_BIG_NUMBER,
8 MEMBER FUNCTION SCIENT_OUTPUT(P_VALUE IN NUMBER) RETURN VARCHAR2,
9 STATIC FUNCTION F_MULTI_SUM_OBJECT(P_IN IN NUMBER, P_SIENCT IN NUMBER DEFAULT NULL) RETURN VARCHAR2
10 );
11 /

类型已创建。

SQL> CREATE OR REPLACE TYPE BODY T_BIG_NUMBER AS
2 STATIC FUNCTION F_T_BIG_NUMBER (P_BIG_NUMBER IN VARCHAR2) RETURN T_BIG_NUMBER AS
3 V_RESULT T_BIG_NUMBER := T_BIG_NUMBER('0', 1);
4 BEGIN
5 V_RESULT.BIG_NUMBER := P_BIG_NUMBER;
6 V_RESULT.NUMBER_LENGTH := LENGTH(P_BIG_NUMBER);
7 RETURN V_RESULT;
8 END;
9
10 MEMBER FUNCTION ADDS (P_ADD IN T_BIG_NUMBER) RETURN T_BIG_NUMBER AS
11 V_FLAG NUMBER(1) DEFAULT 0;
12 V_RESULT T_BIG_NUMBER := T_BIG_NUMBER(NULL, 0);
13 V_SUM NUMBER;
14 V_MAX_LENGTH NUMBER DEFAULT GREATEST(NUMBER_LENGTH, P_ADD.NUMBER_LENGTH);
15 BEGIN
16 FOR I IN 1..V_MAX_LENGTH LOOP
17 V_SUM := TO_NUMBER(NVL(SUBSTR(BIG_NUMBER, -I, 1), '0'))
18 + TO_NUMBER(NVL(SUBSTR(P_ADD.BIG_NUMBER, -I, 1), '0'))
19 + V_FLAG;
20 V_RESULT.BIG_NUMBER := SUBSTR(TO_CHAR(V_SUM), -1, 1) || V_RESULT.BIG_NUMBER;
21 V_FLAG := TRUNC(V_SUM/10);
22 END LOOP;
23 IF V_FLAG != 0 THEN
24 V_RESULT.BIG_NUMBER := TO_CHAR(V_FLAG) || V_RESULT.BIG_NUMBER;
25 END IF;
26 V_RESULT.NUMBER_LENGTH := LENGTH(V_RESULT.BIG_NUMBER);
27 RETURN V_RESULT;
28 END;
29
30 MEMBER FUNCTION MULTIS(P_MULTI IN T_BIG_NUMBER) RETURN T_BIG_NUMBER AS
31 V_RESULT T_BIG_NUMBER := T_BIG_NUMBER('0', 1);
32 V_TEMP T_BIG_NUMBER := T_BIG_NUMBER(NULL, 0);
33 V_MUL NUMBER;
34 BEGIN
35 FOR I IN 1..P_MULTI.NUMBER_LENGTH LOOP
36 FOR J IN 1..NUMBER_LENGTH LOOP
37 V_MUL := TO_NUMBER(SUBSTR(BIG_NUMBER, -J, 1)) * TO_NUMBER(SUBSTR(P_MULTI.BIG_NUMBER, -I, 1));
38 V_TEMP := T_BIG_NUMBER.F_T_BIG_NUMBER(TO_CHAR(V_MUL) || RPAD('0', I + J - 2, '0'));
39 V_RESULT := V_RESULT.ADDS(V_TEMP);
40 END LOOP;
41 END LOOP;
42 RETURN V_RESULT;
43 END;
44
45 MEMBER FUNCTION SCIENT_OUTPUT(P_VALUE IN NUMBER) RETURN VARCHAR2 AS
46 V_VALUE NUMBER DEFAULT LEAST(P_VALUE, NUMBER_LENGTH, 38);
47 V_RETURN VARCHAR2(32767);
48 BEGIN
49 IF V_VALUE <= 0 THEN
50 RAISE_APPLICATION_ERROR(-20000, 'INPUT NUMBER IS NOT CORRECT!');
51 RETURN '0';
52 ELSE
53 V_RETURN := TO_CHAR(TO_NUMBER(SUBSTR(BIG_NUMBER, 1, V_VALUE))
54 + ROUND(TO_NUMBER(NVL(SUBSTR(BIG_NUMBER, V_VALUE + 1, 1), '0')) / 10));
55 RETURN RTRIM(SUBSTR(V_RETURN, 1, 1) || '.' || SUBSTR(V_RETURN, 2), '.')
56 || 'E' || TO_CHAR(NUMBER_LENGTH - 1);
57 END IF;
58 END;
59
60 STATIC FUNCTION F_MULTI_SUM_OBJECT(P_IN IN NUMBER, P_SIENCT IN NUMBER DEFAULT NULL) RETURN VARCHAR2 AS
61 V_MULTI T_BIG_NUMBER := T_BIG_NUMBER('1', 1);
62 V_SUM T_BIG_NUMBER := T_BIG_NUMBER('0', 1);
63 BEGIN
64 FOR I IN 1..P_IN LOOP
65 V_MULTI := V_MULTI.MULTIS(T_BIG_NUMBER(TO_CHAR(I), LENGTH(TO_CHAR(I))));
66 V_SUM := V_SUM.ADDS(V_MULTI);
67 END LOOP;
68 IF P_SIENCT IS NULL THEN
69 RETURN V_SUM.BIG_NUMBER;
70 ELSE
71 RETURN V_SUM.SCIENT_OUTPUT(P_SIENCT);
72 END IF;
73 END;
74
75 END;
76 /

类型主体已创建。

下面就可以调用对象的函数进行计算了:

SQL> SELECT T_BIG_NUMBER.F_MULTI_SUM_OBJECT(5) FROM DUAL;

T_BIG_NUMBER.F_MULTI_SUM_OBJECT(5)
----------------------------------------------------------------------------
153

SQL> SELECT T_BIG_NUMBER.F_MULTI_SUM_OBJECT(100) FROM DUAL;

T_BIG_NUMBER.F_MULTI_SUM_OBJECT(100)
------------------------------------------------------------------------------
94269001683709979260859834124473539872070722613982672442938359305624678223479506023400294093599136466986609124347432647622826870038220556442336528920420940313

现在已经得到了正确的结果。这个对象还增加了一个科学计数法表示的功能:

SQL> SELECT T_BIG_NUMBER.F_MULTI_SUM_OBJECT(100, 5) FROM DUAL;

T_BIG_NUMBER.F_MULTI_SUM_OBJECT(100,5)
------------------------------------------------------------------------
9.4269E157

SQL> SELECT T_BIG_NUMBER.F_MULTI_SUM_OBJECT(100, 4) FROM DUAL;

T_BIG_NUMBER.F_MULTI_SUM_OBJECT(100,4)
------------------------------------------------------------------------
9.427E157

SQL> SELECT T_BIG_NUMBER.F_MULTI_SUM_OBJECT(100, 15) FROM DUAL;

T_BIG_NUMBER.F_MULTI_SUM_OBJECT(100,15)
------------------------------------------------------------------------
9.42690016837100E157

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/7232789/viewspace-481903/,如需转载,请注明出处,否则将追究法律责任。

上一篇: tail命令
请登录后发表评论 登录
全部评论

注册时间:2008-02-17

  • 博文量
    86
  • 访问量
    239461