mejorando la precision 4 - colpos · 2006-12-05 · permite sólo ocho dígitos significativos y...

29
Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91 63 MEJORANDO LA PRECISION NUMERICA DE LOS RESULTADOS DE SIMULACIONES DIGITALES CONTINUAS USANDO ARITMETICA DE PRECISION ARBITRARIA Graciela Bueno 1 , Enrique Arjona 1 , Andrés Arjona 2 RESUMEN En corridas de simulación y particularmente cuando se utilizan modelos digitales continuos, la propagación del error numérico resultante de la ejecución de procesos iterativos puede afectar considerablemente la exactitud de los resultados de la simulación. Algunos lenguajes de programación, como C y Pascal, atenúan la propagación del error a través de permitir el uso de un número fijo relativamente grande de dígitos significativos. Sin embargo, investigar si un número de dígitos significativos es adecuado para un modelo particular no es una tarea fácil para el usuario promedio. Además, si el número máximo de dígitos significativos permitidos por el lenguaje no es satisfactorio, no existe una solución directa para este problema. En este artículo, para atenuar la propagación del error se propone un conjunto de rutinas sencillas para aritmética de precisión arbitraria. La implementación de estas rutinas en programas existentes es directa y una 1 Especialidad de Estadística, Colegio de Postgraduados, Km. 36.5 Carr. México Texcoco, Montecillo 56230 Edo. de México 2 Universidad de las Américas A.C., Puebla Col. Roma, México D.F.

Upload: others

Post on 03-Jul-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

63

MEJORANDO LA PRECISION NUMERICA DE LOS RESULTADOS DE SIMULACIONES DIGITALES

CONTINUAS USANDO ARITMETICA DE PRECISION ARBITRARIA

Graciela Bueno1, Enrique Arjona1, Andrés Arjona2

RESUMEN

En corridas de simulación y particularmente cuando se utilizan

modelos digitales continuos, la propagación del error numérico

resultante de la ejecución de procesos iterativos puede afectar

considerablemente la exactitud de los resultados de la simulación.

Algunos lenguajes de programación, como C y Pascal, atenúan la

propagación del error a través de permitir el uso de un número fijo

relativamente grande de dígitos significativos. Sin embargo, investigar si

un número de dígitos significativos es adecuado para un modelo

particular no es una tarea fácil para el usuario promedio. Además, si el

número máximo de dígitos significativos permitidos por el lenguaje no

es satisfactorio, no existe una solución directa para este problema. En

este artículo, para atenuar la propagación del error se propone un

conjunto de rutinas sencillas para aritmética de precisión arbitraria. La

implementación de estas rutinas en programas existentes es directa y una

1 Especialidad de Estadística, Colegio de Postgraduados, Km. 36.5 Carr. México Texcoco, Montecillo 56230 Edo. de México 2 Universidad de las Américas A.C., Puebla Col. Roma, México D.F.

Page 2: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

64

vez que la implementación se concluye, probar que un número de dígitos

significativos particular es adecuado para el modelo es muy fácil.

Palabras clave: Simulación digital continua, Error de redondeo,

Aritmética de precisión arbitraria

INTRODUCCION

En corridas de simulación, y particularmente al usar modelos

digitales continuos, el cálculo de variables endógenas y de salida

requiere de la retroalimentación de valores intermedios dentro de

procesos iterativos que son ejecutados muchas veces. La propagación

del error numérico resultante de dichos procesos puede afectar

considerablemente la exactitud de los resultados de la simulación.

Los errores numéricos pueden estar inherentes en los parámetros y

en las relaciones aproximadas usadas en las ecuaciones de los modelos

para representar sistemas reales. Estos errores son independientes de la

precisión del lenguaje o la computadora en la que los modelos son

implementados y la única manera de evitarlos es utilizando parámetros y

ecuaciones más precisas.

Además de los errores numéricos inherentes existen dos tipos de

errores que son más bien independientes de la precisión de las

ecuaciones usadas en los modelos.

Page 3: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

65

El primer tipo de error es producido por la naturaleza aproximada

de las rutinas de integración numéricas usadas para extrapolar valores

simulados. Este tipo de error puede ser evitado o atenuado usando

integración simbólica siempre que sea posible, o rutinas de integración

elaboradas y tamaños de paso pequeños (Maron y López 1991).

El segundo tipo de error, que es en el que estamos interesados, es

producido por la precisión numérica fija usada para la representación

interna de los números en una computadora digital. Este tipo de error es

conocido como error de redondeo y se puede propagar. Algunos

lenguajes de programación, como C y Pascal, atenúan la propagación del

error a través de permitir el uso de un número fijo, relativamente largo,

de dígitos significativos. Sin embargo, investigar si un número de dígitos

significativos es adecuado para un modelo particular no es tarea fácil

para el usuario promedio. Además, si el número máximo de dígitos

significativos permitidos por el lenguaje no es satisfactorio, no existe

una solución directa para este problema.

La propagación del error debida a la precisión numérica fija no es

exclusiva de los modelos continuos digitales y ocurre en muchos

algoritmos matemáticos. Con el paso de los años se han propuesto

diversas soluciones a este problema. Las propuestas incluyen el uso de

aritmética entera de precisión ilimitada (Collins, 1966), aritmética

Page 4: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

66

racional (Knuth 1969) y aritmética entera de precisión arbitraria (Knuth

1969). Las maneras en las que estos sistemas aritméticos pueden ser

implementados en una computadora, son usualmente considerados

complejos para el usuario promedio, por lo que no se incluyen en libros

de simulación, estadística u optimización. Por consiguiente, el uso de

sistemas aritméticos especializados ha sido el dominio de paquetes muy

especializados de programación. Otro hecho que posiblemente ha

retrasado la generalización del uso de rutinas de atenuación del error de

redondeo, es que los algoritmos modificados con esas rutinas consumen

más tiempo que los algoritmos originales. El tiempo de cómputo era un

recurso caro hasta la llegada de computadoras personales poderosas lo

cual ocurrió hace apenas algunos años.

Los sistemas computacionales algebraicos como Mathematica

(Wolfram 1991), que permiten el uso de aritmética racional y de

precisión arbitraria, parecen ser una solución a mano para evitar la

propagación del error. Sin embargo, el uso de dichos sistemas para la

implementación de modelos de simulación continuos no ha sido

generalizado aún. Los usuarios usan mayormente otras herramientas

como los lenguajes C, CSL, y SIMULINK (Bennet 1995).

El uso de rutinas de precisión arbitraria no es necesariamente una

tarea compleja. Su complejidad está relacionada íntimamente al tipo de

Page 5: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

67

rutinas utilizadas y la manera en que son implementadas y sus

implementaciones no siempre requieren de la ayuda de un especialista en

ciencias de la computación.

En este artículo, un conjunto de rutinas aritméticas de precisión

arbitraria es propuesto. Estas rutinas pueden proveer una solución a la

propagación del error porque el número de dígitos significativos usados

para la representación numérica en un modelo puede incrementarse tanto

como sea necesario. La implementación de estas rutinas en programas

existentes es directa y requiere básicamente del cambio de expresiones

aritméticas de notación infija a postfija. Una vez que la implementación

de las rutinas en un modelo ha sido realizada, probar que un número

particular de dígitos es adecuado para un modelo es muy fácil y consiste

en comparar los resultados usando diferentes precisiones que difieren en

un pequeño número de dígitos.

El artículo está organizado de la siguiente manera: Primero, se

presenta un ejemplo de propagación del error y se dan generalidades

acerca de la aritmética racional y de precisión arbitraria. Después, las

rutinas de precisión arbitraria propuestas son descritas acompañadas

junto con el proceso para su implementación en programas existentes.

Finalmente una discusión es dada.

Page 6: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

68

PROPAGACION DEL ERROR DE REDONDEO, ARITMETICA

RACIONAL Y PRECISION ARBITRARIA

Como se ha mencionado, el error de redondeo es producido por la

precisión numérica fija usada para la representación interna de números

en una computadora digital. Este ocurre cuando el número de dígitos

significativos disponible para la representación interna de números en

una computadora, es menor que el número de dígitos significativos

obtenidos del resultado de alguna operación aritmética. Una vez que ha

ocurrido un error de redondeo, puede propagarse en operaciones

aritméticas subsecuentes.

Por ejemplo, si tenemos una computadora que internamente

permite sólo ocho dígitos significativos y realizamos la siguiente suma:

1234567 + .1234567 = 1234567.1234567 el resultado contiene catorce dígitos significativos, aún cuando cada uno

de los números sumados contienen sólo siete dígitos significativos. En la

computadora, el resultado será almacenado como 1234567.1 . Eso es, se

han perdido seis dígitos significativos en esta operación.

Para ver como este error de redondeo puede propagarse a más

dígitos significativos, realizaremos dos operaciones más. Primero, con el

resultado obtenido arriba realizamos una simple resta:

Page 7: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

69

1234567.1 - 1234565.1 = 2

y finalmente, con este nuevo resultado, realizamos la siguiente

multiplicación:

2 x 7000 = 14000

El resultado exacto que podríamos haber obtenido al realizar las

operaciones descritas, con un lápiz y papel es 14164.1969. Lo que

significa, que en las operaciones arriba mostradas, a pesar de que se

habían perdido inicialmente sólo seis dígitos, el resultado final es

truncado en siete dígitos significativos debido a la propagación del error.

El ejemplo anterior involucra sólo números con un número finito

de dígitos significativos. Sin embargo, en aritmética real (aritmética con

números reales), se pueden obtener resultados con un número infinito de

dígitos significativos. Por ejemplo:

10 : 3 = 3.333333...

En estos casos, obviamente, el número de dígitos significativos

perdidos en cualquier representación decimal interna es también infinita.

Page 8: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

70

Los números que tienen una representación infinita en un sistema

numérico posicional como el decimal, pueden tener una representación

finita en otro sistema numérico posicional y viceversa. Por ejemplo, si

usamos el sistema ternario (base 3), el número mencionado tendrá la

representación finita 10.1 porque en el sistema ternario:

10.1 = 1 x 3**1 + 0 x 3**0 + 1 x 3**(-1) = 3.33... en base 10

y el número 10.111... , que tiene una representación infinita en el sistema

ternario, es equivalente al número 3.5 en el sistema decimal.

En cualquier sistema numérico posicional, habrá un conjunto

infinito de números que para su representación requieren de un número

infinito de dígitos significativos. Por lo que el cambio de base no es una

solución al problema de error por redondeo.

Para superar el problema de propagación del error de redondeo se

ha propuesto el uso de aritmética racional y de precisión arbitraria.

El uso de aritmética racional en una computadora es soportada por

el hecho de que todos los números decimales finitos y periódicos como

3.3333... y 23.67813813813... , son números racionales y tienen una

representación finita como el cociente de dos enteros. La aritmética

Page 9: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

71

racional siempre producirá números racionales y por lo tanto,

susceptibles a representación finita. Por consiguiente, en el sistema de

números racional, los únicos números reales que no tienen una

representación finita y que son susceptibles al error de redondeo son los

números irracionales como π, e, √2, etc.

Las reglas para transformar números decimales finitos y periódicos

a sus representaciones racionales equivalentes son muy sencillas. Un

número decimal finito puede ser expresado como un número racional

cuyo denominador es una potencia de 10, por ejemplo:

14164.1969 = 141641969/10000

Un número decimal periódico puede ser expresado como la suma

infinita de números racionales, que en turno puede ser expresada como

la suma de dos números racionales y por consiguiente como un número

racional único, por ejemplo:

23.67813813... = 23.67 + .00813 + .00000813 + ...

= 2367/100 + 813/10000 + 813/100000000 + ...

= 2367/100 + 813/99900

= 2365446/99900

Page 10: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

72

Mas aún, los números racionales pueden ser reducidos a números

racionales equivalentes usando el algoritmo de Euclídes para calcular

máximos comunes divisores. Los números reducidos pueden contener

menos dígitos que los números originales. Por ejemplo:

2365446/99900 = 394241/16650

A pesar de su simplicidad, la aritmética racional no es fácil de

implementar en una computadora. Las representaciones de números

racionales como cociente de dos enteros pueden involucrar números

muy largos, por lo que realizar operaciones con ellos puede requerir

precisión infinita de aritmética de enteros. Además, las rutinas para la

reducción de números racionales calculados deben de ser muy eficientes

para minimizar el tiempo de ejecución.

La aritmética de precisión arbitraria permite elegir un número

arbitrario fijo máximo de dígitos significativos para la representación

interna de números en una computadora. Usualmente, el número de

dígitos para la precisión elegida depende de la exactitud requerida en los

resultados de una aplicación en particular. A mayor precisión

seleccionada, mayor será el tiempo que las aplicaciones requerirán para

su ejecución.

Page 11: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

73

La precisión arbitraria es más fácil de implementar que la

aritmética racional. Sin embargo, su utilidad para atenuar la propagación

del error de redondeo es más débil que la utilidad de la aritmética

racional. Con precisión arbitraria, todos los números decimales infinitos

almacenados en una computadora, racionales e irracionales, tendrán

error de redondeo y podrían causar la propagación del error. Más aún,

números decimales finitos pueden también tener error de redondeo

dependiendo de su número de dígitos significativos. Sin embargo, en

comparación con la máxima precisión permitida en muchos lenguajes de

programación, la cual es del orden de 20 dígitos significativos, el uso de

precisión arbitraria de 50 o más dígitos, puede llegar a ser muy

satisfactoria.

La implementación de precisión arbitraria en una computadora

puede realizarse de diversas formas. Desde unas muy eficientes, usando

lenguaje máquina y optimizando la ejecución y la cantidad de memoria

de la computadora. A unos muy simples usando un lenguaje de alto nivel

y un byte para almacenar cada dígito decimal. Las implementaciones

simples son más fáciles de rastrear y requieren de menos programación,

pero usualmente son lentas. Implementaciones completas requieren de

una librería extensa de funciones matemáticas. Las rutinas presentadas

en la siguiente sección contienen características eficientes, pero pueden

considerarse simples.

Page 12: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

74

UN CONJUNTO DE RUTINAS DE PRECISION ARBITRARIA

Las rutinas aquí presentadas han sido implementadas como

procedimientos en Pascal, pero pueden fácilmente ser cambiadas a

funciones o subrutinas en otro lenguaje de alto nivel como C o

FORTRAN. El almacenamiento interno de números y operaciones

aritméticas es realizado en base 256, por lo que cada dígito requiere un

byte. En los procedimientos, AP significa precisión arbitraria; EP

precisión externa; e IP precisión interna. AP (un tipo); EP (una

constante); e IP (también una constante) son declaradas globales en el

programa principal.

Las rutinas han sido clasificadas en cuatro grupos. El primer grupo

consiste de los procedimientos READLONG y WRITELONG usadas

para entrada y salida. El segundo grupo está integrado por las funciones

GREATERLONG, LESSLONG, y EQULONG usadas para pruebas de

relación. El tercer grupo se refiere a los procedimientos CONSTLONG,

ADDLONG, SUBLONG, MULLONG, y DIVLONG usadas para

operaciones aritméticas. El último grupo consiste de los procedimientos

de soporte INITLONG, CONVLONG, NORMLONG y ROUNDLONG.

Las rutinas en este último grupo no son llamadas por el usuario. A

continuación se presentan las rutinas.

Page 13: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

75

Grupo 1 READLONG PROCEDURE READLONG(VAR APVAR:AP); { Este procedimiento lee un número decimal de punto fijo de precisión arbitraria desde el } {teclado y almacena su representación de precisión arbitraria de base BASE en APVAR } var inputstr:string[EP+2]; begin readln(inputstr); CONVLONG(inputstr, APVAR); end; WRITELONG PROCEDURE WRITELONG(APVAR:AP); { Este procedimiento despliega en la pantalla la representación decimal de punto fijo de} {número de precisión arbitraria arbitraria en base BASE almacenada en APVAR } var appart,digit,ten,aptemp:AP; ndigits,i,exp:integer; outputstr:string[EP+2]; decdigit:string[1]; begin INITLONG(10,ten); outputstr:=''; exp:=APVAR[IP+1]-127; ndigits:=0; if APVAR[1]=0 then begin outputstr:='0'; writeln(outputstr); exit; end; if exp>0 then begin appart:=APVAR; appart[0]:=1; for i:=exp+1 to IP do appart[i]:=0; repeat DIVLONG(appart,ten,aptemp); for i:=aptemp[IP+1]-127+1 to IP do aptemp[i]:=0; MULLONG (aptemp,ten,aptemp); SUBLONG(appart,aptemp,digit); if digit[IP+1]>127 then begin str(digit[1]:1,decdigit); outputstr:=decdigit+outputstr; end

Page 14: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

76

else outputstr:='0'+outputstr; ndigits:=ndigits+1; DIVLONG(aptemp,ten,aptemp);appart:=aptemp; until (appart[IP+1]=127); exp:=APVAR[IP+1]-127; i:=exp+1; while (APVAR[i]=0) and (i<=IP) do i:=i+1; if i=IP+1 then begin if APVAR[0]=0 then outputstr:='-'+outputstr; writeln(outputstr); exit; end; end; outputstr:=outputstr+'.'; appart:=APVAR; exp:=APVAR[IP+1]-127; if exp>0 then begin i:=exp+1; while (i<=IP) and (appart[i]=0) do begin exp:=exp+1; i:=i+1; end; for i:=exp+1 to IP do appart[i-exp]:=appart[i]; for i:=IP-exp to IP do appart[i]:=0; appart[ip+1]:=appart[IP+1]-exp; end; appart[0]:=1; repeat MULLONG(appart,ten,digit); if digit[IP+1]=128 then begin str(digit[1]:1,decdigit); outputstr:=outputstr+decdigit; appart:=digit; for i:=2 to IP do digit[i]:=0; SUBLONG(appart,digit,appart); end else begin outputstr:=outputstr+'0'; MULLONG(appart,ten,appart); end; ndigits:=ndigits+1; until ((ndigits=EP) or (appart[1]=0)); if apvar[0]=0 then outputstr:='-'+outputstr; writeln(outputstr); end;

Page 15: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

77

Grupo 2 GREATERLONG FUNCTION GREATERLONG(OP1,OP2:AP) : BOOLEAN; { Esta función compara variables de precisión arbitraria OP1 y OP2, si OP1>OP2 regresa} {un valor verdadero(true) } var pos:byte; begin GREATERLONG:=true; if OP1[1]=0 then if (OP2[0]=1) or (OP2[1]=0) then begin GREATERLONG:=false; exit; end else exit; if OP2[1]=0 then if OP1[0]=0 then begin GREATERLONG:=false; exit; end else exit; if (OP1[0]=0) and (OP2[0]=1) then begin GREATERLONG:=false; exit; end; if (OP1[0]=1) and (OP2[0]=0) then exit; if OP1[0]=1 then begin if OP1[IP+1]<OP2[IP+1] then begin GREATERLONG:=false; exit;end else if OP1[IP+1]>OP2[IP+1] then exit; end; if OP1[0]=0 then begin if OP1[IP+1]>OP2[IP+1] then begin GREATERLONG:=false; exit; end else if OP1[IP+1]<OP2[IP+1] then exit; end; pos:=1; while (OP1[pos]=OP2[pos]) and (pos<IP) do pos:=pos+1; if OP1[0]=1 then GREATERLONG:=OP1[pos]>OP2[pos] else GREATERLONG:=OP1[pos]<OP2[pos]; end;

Page 16: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

78

LESSLONG FUNCTION LESSLONG(OP1,OP2:AP) : BOOLEAN; { Esta función compara variables de precisión arbitraria OP1 y OP2, } { si OP1<OP2 regresa un valor verdadero(true) } begin LESSLONG:=GREATERLONG(OP2,OP1); end; EQULONG FUNCTION EQULONG(OP1,OP2:AP) : BOOLEAN; { Esta función compara variables de precisión arbitraria OP1 y OP2, } { si OP1=OP2 regresa un valor verdadero(true) } begin EQULONG:=not GREATERLONG(OP1,OP2) and not GREATERLONG(OP2,OP1); end; Grupo 3 CONSTLONG PROCEDURE CONSTLONG(REALCONST:REAL; APVAR:AP); { Este procedimiento almacena el valor de la constante real REALCONST } { en la variable de precisión arbitraria APVAR } var apstring:string[EP+2]; begin str(REALCONST,apstring); CONVLONG(apstring, APVAR); end; ADDLONG PROCEDURE ADDLONG (OP1,OP2:AP; VAR RES:AP); { Este procedimiento suma los dos números de precisión } { arbitraria OP1 y OP2 y almacena el resultado en RES } var exp1,exp2,difexp,i,carry,shift:integer; aptemp:AP;

Page 17: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

79

acum: array[0..2*IP+1] of longint; begin INITLONG(0,aptemp); for i:=1 to 2*IP+1 do acum[i]:=0; exp1:=OP1[IP+1]-127; exp2:=OP2[IP+1]-127; if OP1[1]=0 then begin RES:=OP2; exit; end else if OP2[1]=0 then begin RES:=OP1;exit; end; if exp2<=exp1-IP then begin RES:=OP1; exit; end else if exp1<=exp2-IP then begin RES:=OP2; exit; end; if OP1[0]<>OP2[0] then begin if OP2[0]=0 then OP2[0]:=1 else OP2[0]:=0; SUBLONG(OP1,OP2,RES); exit; end; if exp1<exp2 then begin aptemp:=OP1; OP1:=OP2; OP2:=aptemp; exp1:=OP1[IP+1]; exp2:=OP2[IP+1]; end; acum[0]:=OP1[0]; acum[2*IP+1]:=IP+OP2[IP+1]; difexp:=exp1-exp2; shift:=2*IP-difexp+1; if difexp<>0 then acum[shift]:=OP2[IP-difexp+1]; carry:=0; for i:=difexp to IP+difexp-1 do begin shift:=shift-1; if i<IP then acum[shift]:=OP2[IP-i]+carry+OP1[IP-i+difexp] else acum[shift]:=carry+OP1[IP-i+difexp]; if acum[shift]>=BASE then begin carry:=1; acum[shift]:=acum[shift]-BASE; end else carry:=0; end; acum[shift-1]:=carry; NORMLONG(acum); TRUNCLONG(acum,RES); end; SUBLONG PROCEDURE SUBLONG (OP1,OP2:AP; VAR RES:AP); { Este procedimiento resta los dos números de precisión } { arbitraria OP1 y OP2 y almacena el resultado en RES } var exp1,exp2,difexp,i,carry,shift,sop1,sop2:integer; aptemp:AP;

Page 18: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

80

acum: array[0..2*IP+1] of longint; begin if OP2[0]=1 then OP2[0]:=0 else OP2[0]:=1; INITLONG(0,aptemp); for i:=1 to 2*IP+1 do acum[i]:=0; exp1:=OP1[IP+1]-127; exp2:=OP2[IP+1]-127; if OP1[1]=0 then begin RES:=OP2; if RES[1]=0 then RES[0]:=1; exit; end else if OP2[1]=0 then begin RES:=OP1; exit; end; if exp2<=exp1-IP then begin RES:=OP1; exit; end else if exp1<=exp2-IP then begin RES:=OP2; exit; end; if OP1[0]=OP2[0] then begin ADDLONG(OP1,OP2,RES); exit; end; sop1:=OP1[0]; sop2:=OP2[0]; OP1[0]:=1; OP2[0]:=1; if LESSLONG(OP1,OP2) then begin OP1[0]:=sop1; OP2[0]:=sop2; aptemp:=OP1; OP1:=OP2; OP2:=aptemp; exp1:=OP1[IP+1]; exp2:=OP2[IP+1]; end else begin OP1[0]:=sop1; OP2[0]:=sop2; end; acum[0]:=OP1[0]; acum[2*IP+1]:=IP+OP2[IP+1]; difexp:=exp1-exp2; shift:=2*IP-difexp+1; if difexp<>0 then acum[shift]:=OP2[IP-difexp+1]; carry:=0; for i:=difexp to IP+difexp-1 do begin shift:=shift-1; sop1:=OP1[IP-i+difexp]; sop1:=sop1+carry; if i<IP then sop2:=OP2[IP-i] else sop2:=0; if sop1<sop2 then begin carry:=-1; sop1:=sop1+BASE; end else carry:=0; if i<IP then acum[shift]:=sop1-sop2 else acum[shift]:=sop1; end; acum[shift-1]:=carry; NORMLONG(acum); TRUNCLONG(acum,RES); end;

Page 19: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

81

MULLONG PROCEDURE MULLONG (OP1,OP2:AP; VAR RES:AP); { Este procedimiento multiplica los dos números de precisión } { arbitraria OP1 y OP2 y almacena el resultado en RES } var i,j,twoip,p1,p2,shift:integer; carry,op1long:longint; acum:array[0..2*IP+1] of longint; begin if (OP1[1]=0) or (OP2[1]=0) then begin INITLONG(0,RES); exit; end; if OP1[0]=OP2[0] then acum[0]:=1 else acum[0]:=0; carry:=0; twoip:=2*IP; for i:=1 to twoip+1 do acum[i]:=0; p1:=IP; while (p1>1) and (OP1[p1]=0) do p1:=p1-1; p2:=IP; while (p2>1) and (OP2[p2]=0) do p2:=p2-1; shift:=twoip-p1-p2; for i:=twoip downto shift+1 do begin acum[i-shift]:=carry; for j:=0 to twoip-i do if (p2-j>0) and (p1-twoip+i+j>0) then begin op1long:=OP1[p1-twoip+i+j]; acum[i-shift]:=acum[i-shift]+OP2[p2-j]*op1long;end; carry:=acum[i-shift] div BASE; acum[i-shift]:=acum[i-shift] mod BASE; end; acum[twoip+1]:=OP1[IP+1]-127+OP2[IP+1]; NORMLONG(acum); TRUNCLONG(acum,RES); end; DIVLONG PROCEDURE DIVLONG (OP1,OP2:AP; VAR RES:AP); { Este procedimiento divide el número de precisión arbitraria OP1 } { por el número de precisión arbitraria OP2 y almacena el resultado en RES } var sign:byte; pos,i,reduce,expres:integer; aptemp:AP; acum:array[0..2*IP+1] of longint; dividp,divisp,quotp,lbase:longint; begin

Page 20: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

82

if OP1[0]=OP2[0] then sign:=1 else sign:=0; OP1[0]:=1;OP2[0]:=1; if OP2[1]=0 then begin writeln('arbitrary precision divide by cero'); halt; end; INITLONG(1,aptemp); for i:=1 to 2*IP+1 do acum[i]:=0; if EQULONG(OP2,aptemp) then begin RES:= OP1; RES[0]:=sign; exit;end; if OP1[1]=0 then begin INITLONG(0,RES); exit; end; acum[2*IP+1]:=OP1[IP+1]+1-OP2[IP+1]+127; reduce:=2+127-OP2[IP+1]; expres:=acum[2*IP+1]; if (expres<0) or (expres>255) or (expres>127+IP) then begin writeln('arbitrary division invalid'); halt; end; OP2[IP+1]:=OP2[IP+1]+reduce; OP1[IP+1]:=2+127; pos:=0; lbase:=BASE; divisp:=OP2[1]*lbase+OP2[2]; while (OP1[1]>0) and (pos<=IP+1) do begin dividp:=OP1[1]; i:=128; while OP1[IP+1]>i do begin i:=i+1; dividp:=dividp*lbase+OP1[i-127]; end; if dividp >= divisp then begin quotp:=dividp div divisp; while quotp >= BASE do begin quotp:=quotp div BASE; OP1[IP+1]:=OP1[IP+1]-1; end; INITLONG(quotp,aptemp); MULLONG(aptemp,OP2,aptemp); while GREATERLONG(aptemp,OP1) do begin quotp:=quotp-1; SUBLONG(aptemp,OP2,aptemp); end; SUBLONG(OP1,aptemp,OP1); OP1[IP+1]:=OP1[IP+1]+1; pos:=pos+1; acum[pos]:=quotp; end else begin pos:=pos+1; acum[pos]:=0; OP1[IP+1]:=OP1[IP+1]+1; end; end; acum[0]:=sign; NORMLONG(acum); TRUNCLONG(acum,RES); end;

Page 21: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

83

Grupo 4 INITLONG PROCEDURE INITLONG(NUMBER:longint; VAR APVAR:AP); { Este procedimiento inicializa la variable de precisión arbitraria APVAR } { con un dígito no negativo < BASE*BASE-1 dado en NUMBER } var pos:byte; begin for pos:=1 to IP do APVAR[pos]:=0; APVAR[IP+1]:=127;APVAR[0]:=1; if NUMBER>0 then begin if number>=BASE then begin APVAR[1]:=NUMBER div BASE; APVAR[2]:=number mod BASE; APVAR[IP+1]:=129; end else begin APVAR[1]:=NUMBER; APVAR[IP+1]:=128; end; end; end; CONVLONG PROCEDURE CONVLONG(VAR APSTRING; VAR APVAR:AP); { Este procedimiento convierte un número decimal de punto fijo de precisión arbitraria } { en la cadena APSTRING a su representación de precisión arbitraria en base BASE y } { la almacena en APVAR } var inputstr:string[EP+2] absolute APSTRING; ten,digit,powers,left,right:AP; sign:byte; pos,point:integer; begin INITLONG(10,ten); INITLONG(1,powers); INITLONG(0,left); INITLONG(0,right); INITLONG(0,APVAR); pos:=0; repeat pos:=pos+1; until (inputstr[pos]<>' '); inputstr:=copy(inputstr,pos,length(inputstr)-pos+1); sign:=1; if inputstr[1]='-' then sign:=0; if (inputstr[1]='+') or (inputstr[1]='-') then inputstr:=copy(inputstr,2,length(inputstr)-1);

Page 22: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

84

point:=0; for pos:=1 to length(inputstr) do if inputstr[pos]='.' then point:=pos; if point<>1 then begin if point=0 then point:=length(inputstr)+1; for pos:=point-1 downto 1 do if (inputstr[pos] in ['0'..'9']) then begin INITLONG(0,digit); digit[IP+1]:=128; digit[1]:=ord(inputstr[pos])-48; MULLONG(powers,digit,digit); ADDLONG(left,digit,left); MULLONG(powers,ten,powers);end else begin writeln('arbitrary precision invalid input data'); halt; end; APVAR:=left; APVAR[0]:=sign; end; if point >= length(inputstr) then exit; inputstr:=copy(inputstr,point+1,length(inputstr)-point); INITLONG(1,powers); DIVLONG(powers,ten,powers); for pos:=1 to length(inputstr) do if (inputstr[pos] in ['0'..'9']) then begin INITLONG(0,digit); digit[IP+1]:=128; digit[1]:=ord(inputstr[pos])-48; MULLONG(powers,digit,digit); ADDLONG(right,digit,right); DIVLONG(powers,ten,powers);end else begin writeln('arbitrary precision invalid input data'); halt; end; ADDLONG(LEFT,RIGHT,APVAR); APVAR[0]:=sign; end; NORMLONG PROCEDURE NORMLONG(VAR UNNORMED); { Este procedimiento normaliza un número doble de precisión arbitraria APVAR } var acum:array[0..2*IP+1] of longint absolute UNNORMED; i,j,limit,twoip:integer; begin twoip:=2*IP; limit:=acum[twoip+1]; if twoip<limit then limit:=twoip; i:=0; while (acum[i+1]=0) and (i<=limit) do i:=i+1;

Page 23: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

85

if i>0 then begin for j:=1 to twoip-i do acum[j]:=acum[j+i]; for j:=twoip+1-i to twoip do acum[j]:=0; acum[twoip+1]:=acum[twoip+1]-i; end; if acum[twoip+1]-127>IP then begin writeln('arbitrary precision overflow');halt;end; if acum[1]=0 then begin for i:=2 to twoip do if acum[i]<>0 then begin writeln('arbitrary precision underflow'); halt; end; acum[0]:=1; acum[twoip+1]:=127;end; end; TRUNCLONG PROCEDURE TRUNCLONG(VAR UNROUND; VAR APVAR:AP); { Este procedimiento trunca un número doble de precisión arbitraria UNROUND y } { almacena el resultado en el número de precisión arbitraria APVAR } var i,pos:integer; acum:array[0..2*IP+1] of longint absolute UNROUND; begin APVAR[IP+1]:=acum[2*IP+1]; if ROUND=0 then begin for pos:=0 to IP do APVAR[pos]:=acum[pos]; exit; end else begin APVAR[0]:=acum[0]; if acum[IP+1]>=trunc(BASE/2) then acum[IP]:=acum[IP]+1; for pos:=IP downto 2 do if acum[pos]=BASE then begin APVAR[pos]:=0; acum[pos-1]:=acum[pos-1]+1; end else APVAR[pos]:=acum[pos]; if acum[1]=BASE then begin for pos:=IP downto 3 do APVAR[pos]:=APVAR[pos-1]; APVAR[1]:=1; APVAR[2]:=0; APVAR[IP+1]:=APVAR[IP+1]+1;end else APVAR[1]:=acum[1]; end;

Page 24: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

86

for i:=0 to IP*2+1 do acum[i]:=0; end;

IMPLEMENTACION DE PRECISION ARBITRARIA EN

PROGRAMAS EXISTENTES

La implementación de las rutinas mencionadas en programas

existentes es directa. Toda la aritmética real en sus programas es

transformada en aritmética de precisión arbitraria. Las reglas para la

implementación son las siguientes:

1. Incluya en su programa principal (main) la declaración global de

BASE, ROUND, EP, IP y AP. BASE es la base utilizada internamente

por las rutinas y puede ser cualquier número desde 10 a 256. Las bases

que son múltiplos de 10 usualmente se desempeñan más rápidamente

que otras bases. EP es la precisión deseada (máximo número de dígitos

significativos). El máximo valor permitido por EP en las rutinas dadas es

256, sin embargo, cuando los números son introducidos por el teclado, el

número máximo para EP será 130. IP es la precisión interna equivalente

en bytes de EP y depende de la base usada, para base 10 IP=EP; para

base 256 IP=1/2EP. Para otras bases use valores intermedios para IP y

revise la adecuidad. ROUND es utilizado para indicar si se desea

redondeo interno, un valor de 1 significa un redondeo al siguiente dígito

Page 25: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

87

significativo, un valor de 0 significa truncar sin redondear. AP es la

declaración del tipo de precisión arbitraria. Las declaraciones a incluir

en el programa principal, antes de la primera declaración BEGIN son

similares a las siguientes:

CONST EP=100; IP=50; BASE=250; ROUND=1;

TYPE AP=ARRAY[0..IP+1] OF BYTE;

2. Incluya en su programa principal y también antes de la primera

declaración begin, los encabezados de todas las rutinas con la opción

forward, y después del grupo de encabezados, el código de todas las

rutinas. Por ejemplo, el encabezado del procedimiento READLONG es:

PROCEDURE READLONG(VAR APVAR:AP); forward;

3. Incluya en su programa principal la declaración de las variables de

precisión arbitraria que serán utilizadas para almacenar resultados

intermedios de operaciones aritméticas (paso 5). Usualmente, dos

variables serán suficientes, pero use más si así lo desea. Las variables

temporales podrán ser declaradas de la siguiente manera:

VAR TEMP1, TEMP2: AP;

Page 26: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

88

si su programa ya usa variables con esos nombres, use otros nombres

para las variables temporales.

4. Cambie todas las variables de tipo real utilizadas en su programa al

tipo AP. Por ejemplo, si en su programa existe una declaración como la

siguiente:

VAR A,B,C: REAL;

cámbiela a: VAR A,B,C: AP;

5. Cambie todas las declaraciones de lectura (escritura) de datos reales

en su programa a llamadas al procedimiento READLONG

(WRITELONG). Estos procedimientos sólo leen (escriben) una variable

a la vez, por lo que una declaración puede requerir más de una llamada a

READLONG (WRITELONG). Por ejemplo, si en su programa hay una

declaración como la siguiente (A y B son variables reales):

READLN (A,B);

cámbiela a: READLONG(A); READLONG(B);

6. Cambie todas las expresiones aritméticas que involucren datos reales

en su programa a llamadas a los procedimientos aritméticos

Page 27: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

89

ADDLONG, SUBLONG, MULLONG y DIVLONG. Note que esos

procedimientos realizan operaciones sólo entre dos números, por lo que

puede ser necesario reorganizar una expresión en su programa en varias

llamadas a los procedimientos mencionados. Los resultados intermedios

son almacenados en variables temporales de precisión arbitraria (véase

paso 2). Si una expresión contiene constantes reales, éstas deberán ser

transformadas a precisión arbitraria utilizando el procedimiento

CONSTLONG. Por ejemplo, si su programa contiene una expresión

como la siguiente (A, B, C y D son variables reales):

A:= (B+2.0)/(C*D)

cámbiela a:

CONSTLONG(2.0,TEMP1); ADDLONG(B,TEMP1,TEMP1);

MULTLONG(C,D,TEMP2); DIVLONG(TEMP1,TEMP2,A)

7. Cambie todas las expresiones relacionales que involucren datos reales

en su programa, a llamadas a los procedimientos GREATERLONG,

LESSLONG, y EQULONG. Por ejemplo, si su programa contiene la

siguiente declaración (A, B y C son variables reales):

IF A<B THEN C=A+B

cámbiela a: IF LESSLONG(A,B) THEN ADDLONG(A,B,C);

Page 28: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Graciela Bueno, Enrique Arjona y Andrés Arjona

90

DISCUSION

Es bien conocido el hecho que los algoritmos iterativos de

integración numérica pueden desempeñarse de manera pobre en muchas

circunstancias. Esto es especialmente cierto cuando se trabaja con

modelos que contienen ecuaciones diferenciales parciales cuya

resolución numérica requiere la solución de grandes sistemas de

ecuaciones simultáneas. El análisis de regresión es otra área en la que

podemos terminar con resultados no deseables. Una matriz de

covarianza inestable puede producir una inversa sin sentido. La

precisión arbitraria puede ser muy valiosa en dichos casos. Las rutinas

presentadas aquí, en una versión ligeramente distinta, han probado ser de

gran ayuda en estudios de caso de finanzas y ciencias de la vida en los

que los procedimientos usuales fallaron.

La precisión arbitraria puede también ser de gran ayuda para

implementar algoritmos que son fácilmente entendibles pero

numéricamente inestables. En estos casos, la precisión arbitraria puede

hacer innecesario el conocimiento de técnicas sofisticadas de análisis

numérico. De hecho, las rutinas presentadas, implementadas en una

computadora 486-DX4 corriendo a 100 MHz con 8 megabytes de RAM,

han sido probadas con algoritmos ineficientes en problemas numéricos

clásicos y se han desempeñado muy bien, tanto en términos de tiempo de

ejecución, como de exactitud.

Page 29: Mejorando la Precision 4 - COLPOS · 2006-12-05 · permite sólo ocho dígitos significativos y realizamos la siguiente suma: 1234567 + .1234567 = 1234567.1234567 el resultado contiene

Comunicaciones en Socioeconomía, Estadística e Informática 2002, Vol. 6 Núm. 1, pp 63-91

91

Finalmente, a pesar de que las rutinas dadas fueron desarrolladas

para aritmética real de precisión arbitraria, las rutinas pueden utilizarse

también para aritmética entera de precisión arbitraria implícita. En este

caso, las variables enteras del programa del usuario que requieran de

precisión arbitraria deben ser convertidas primero a reales, y luego a

precisión arbitraria. Una rutina adicional para pruebas de derrame

(overflow) podría ser muy conveniente.

Agradecimientos: Este artículo fue publicado con apoyo del

CONACYT a través del Proyecto 31984-B

REFERENCIAS

Bennett, B.S. 1995. Simulation Fundamentals. Prentice Hall

International, U.K. Collins, G.E. 1966. "PM-A System for Polynomial Manipulation."

Communications of the ACM, Vol. 9, no. 8: 578-579. Conte, S.D. and C. de Boor 1980. Elementary Numerical Analysis. An

Algorithmic Approach. McGraw-Hill International Book Company, Tokyo, Japan.

Knuth, D.E. 1969. The Art of Computer Programming, Vol II : Seminumerical Algorithms. Addison-Wesley, Reading, MA.

Maron, M.J. and R.J. Lopez. 1991. Numerical Analysis: A practical Approach. Wasdworth Publishing Companing,

Wolfran, S. 1991. Mathematica: A System for Doing Mathematics by Computer. Addison-Wesley Publishing Company, Menlo Park, CA.