Bueno, este documento es una breve iniciación al funcionamiento del coprocesador matemático en los sistemas PC y, más concretamente, a la arquitectura de los procesadores PENTIUM, puesto que hoy por hoy es lo más utilizado. No es mi intencion dar una explicacion completa de su arquitectura interior, simplemente trato de arrojar un poco de luz donde no la hay, porque cuando decidà meterme con el coprocesador matemático, no encontre apenas documentación y la poca (por no decir nula) estaba en inglés.
¿Merece la pena usar el copro?
Esta pregunta es algo ambigua, porque depende mucho de la aplicacion en sÃ. Si lo que queremos realizar son calculos intensivos en 3D la respuesta es sÃ. Esto depende mucho por la incapacidad que tiene el coprocesador de intercambiar datos con los registros de proposito general, lo que obliga a costosas descargas en memoria con conversion a entero y que provoca que, por ejemplo, un calculo en el que tengamos que utilizar directamente el resultado de la operacion como un puntero a una zona de memoria, o como valor para introducir en una zona de memoria, sea algo lento.
Si por ejemplo lo que queremos es realizar un calculo sobre unos datos dados (por ejemplo una multiplicacion de matrices) si que merece la pena su uso.
Estructura interna
El coprocesador trabaja internamente sólo en formato real, por lo que cualquier carga en los registros de coprocesador provocará que dicho valor sea convertido a coma flotante.
Sus registros están estructurados en forma de pila y se accede a ellos por el numero de entrada que ocupan en la pila.
Los registros son R(0) hasta R(7), en total ocho registros de 80bits, como han de ser manejados en formato de pila, el coprocesador tiene un puntero de control de pila llamado St, Toda interacción que tengamos que hacer con los registros del coprocesador se realiza a traves del puntero de pila St, donde el último valor introducido es St o St(0) y si hubieramos rellenado todos los registros el ultimo seria St(7)... ¿ok? Por ejemplo:
- Cargar en copro dato (1345)
- ahora St(0)=1345
- Cargar en copro dato (5431)
- ahora St(0)=5431 y St(1)=1345
Tipos de datos
El coprocesador puede obtener y escribir datos en memoria de los siguientes tipos.
Entero | Words(16bits),Dword(32 bits),Qwords(64 bits) |
Real | Words(16 bits),Dword(32 bits),Qwords(64 bits ),Twords(80 bits) |
Un poco de materia...
Bien, ya hemos visto como ordena el coprocesador sus registros, ahora vamos a ver alguna operacion sencilla.
Todos los ejemplos que aquà escriba estarán enteramente en ensamblador, puesto que si lo que queremos es velocidad de poco nos sirve la math387.lib del C.
.386 .Model Flat .Stack 800h .DATA Numero1 dd 25 ; Numero en formato entero Numero2 dd 1.25 ; Numero en formato real ; (¡Ojo! este numero solo puede ser ; accedido correctamente por el copro ; a no ser que nos hagamos una rutina ; conversora de formato de FPU a entero ; cosa que no merece la pena, porque ; es mas facil cargarlo y convertirlo ; con el copro, y mas rapido) Resul dd ? ; Variable para el resultado Desca dd ? ; Variable para correccion de pila .CODE Start: fild Dword Ptr ds:[Numero1] ;Cargar en el copro la ;variable Numero1 indicando ;que es un entero fld Dword Ptr ds:[Numero2] ;Idem pero es un real fadd St(0),St(1) ;Sumarlos ;St(0)=St(0)+St(1) fstp Dword Pt ds:[Resul] ;Descargar el resultado. fstp Dword Ptr ds:[Desca] ;Descartar el otro valor. mov eax,4c00h int 21h End Start
Como hemos visto al funcionar a base de pila siempre tenemos que dejarla tal y como se encontraba al iniciarse nuestra rutina, de ahi la operacion de popeo con la variable Desca, aunque esto puede realizarse más rapido, siempre teniendo en cuenta varias cosas.
En vez de usar fadd podemos usar faddp que automaticamente suma St(0) y St(1) y elimina automáticamente el operando sobrante de la pila quedando en St(0) el resultado listo para ser popeado.
¡Ojo, las instrucciones de cálculo con la particula "p" ej: faddp o fdivp, sólo trabajan con las posiciones de pila St(0) y St(1)!
También podrÃamos haber usado en vez del fstp a Descal un ffree St(1), esta instrucción automáticamente libera ese componente de la pila, pero sólo es efectiva para dejar la pila tal y como estaba cuando el operando a liberar ocupa la última posicion en la pila (esto es, el primero introducido).
Start: fild Dword Ptr ds:[Numero1] ;Cargar en el copro la ;variable Numero1 indicando ;que es un entero fld Dword Ptr ds:[Numero2] ;Idem pero es un real faddp ;Sumarlos y popear el ;operando sobrante. ;St(0)=St(0)+St(1) ;pop st(1) > nul ;Solo opera con estos ;punteros de pila!! fstp Dword Ptr ds:[Resul] ;Descargar el resultado. mov eax,4c00h int 21h End Start
o bien...
.CODE Start: fild Dword Ptr ds:[Numero1] ;Cargar en el copro ;la variable Numero1 ;indicandole que es un entero fld Dword Ptr ds:[Numero2] ;Idem pero es un real fadd St(0),St(1) ;Sumarlos ;St(0)=St(0)+St(1) fstp Dword Ptr ds:[Resul] ;Descargar el resultado. ;pop St(0) > Resul ffree St(0) ;Descartar el otro valor. ;Free St(0) (stack cleared) mov eax,4c00h int 21h End Start
Como habreis podido observar liberamos con ffree St(0) esto es por lo anteriormente comentado. Al liberar el resultado de la pila St(1) pasa a ser St(0), St(2)-St(1), etc. Debido a su estructura en forma de pila.
Instrucciones
Estas son las instrucciones mas comunes, he omitido algunas, por tratarse por ejemplo de aritmetica BCD (cosa poco comun en el mundo del tiempo-real) pero para los interesados, Intel tiene a su disposicion unos archivos en formato de Acrobat Reader con la lista y la organizacion interna de la FPU. Los ciclos de reloj de las instrucciones, son los declarados por Intel para el Pentium basico, esto puede verse alterado en las CPU's MMX, PRO y Pentium II. No voy a comentar las instrucciones porque en este momento carezco del tiempo necesario para realizar una documentación sobre las instrucciones de manera seria, pero de todas formas solo hay que ser un poco despierto para con solo los nombres hacerse una idea basica del funcionamiento de cada instrucción. Cualquier instruccion de cálculo ( fadd, fsub, etc) toman por defecto si no se le especifica otros operandos St(0) y St(1).
Leyenda: rm=Modo Real, vm=Modo Virtual, pm=Modo Protegido
Instrucción | Ejemplo | Ciclos de reloj |
---|---|---|
FABS | fabs | 1 |
FADD [reg,reg] | fadd | 3, 1 |
FADD memreal | fadd shortreal | 3, 1 |
FADDP reg,ST | faddp st(6),st | 3, 1 |
FIADD memint | fiadd int16 | 7, 4 |
FCHS | fchs | 1 |
FCLEX | fclex | 9+ |
FNCLEX | fnclex | 9 |
FCOM | fcom | 4, 1 |
FCOMP | fcomp | 4, 1 |
FCOMPP | fcompp | 4, 1 |
FICOM memint | ficom double | 8, 4 |
FICOMP memint | ficomp darray[di] | 8, 4 |
FCOS | fcos | 18-124 |
FDECSTP | fdecstp | 1 |
FDIV [reg,reg] | fdiv st(5),st | 39 |
FDIV memreal | fdiv longreal | 39 |
FDIVP reg,ST | fdivp st(6),st | 39 |
FIDIV memint | fidiv warray[di] | 42 |
FDIVR [reg,reg] | fdivr st(5),st | 39 |
FDIVR memreal | fdivr longreal | 39 |
FDIVRP reg,ST | fdivrp st(6),st | 39 |
FIDIVR memint | fidivr warray[di] | 42 |
FFREE ST(i) | ffree st(3) | 1 |
FILD memint | fild quads[si] | 3, 1 |
FINCSTP | fincstp | 1 |
FINIT | finit | 16 |
FNINIT | fninit | 12 |
FIST memint | fist doubles[8] | 6 |
FISTP memint | fistp longint | 6 |
FLD reg | fld st(3) | 1 |
FLD mem32real | fld longreal | 1 |
FLD mem64real | Â |
1 |
FLD mem80real | Â |
3 |
FLD1 | fld1 | 2 |
FLDZ | fldz | 2 |
FLDPI | fldpi | 5,3 |
FLDL2E | fldl2e | 5, 3 |
FLDL2T | fldl2t | 5, 2 |
FLDLG2 | fldlg2 | 5, 3 |
FLDLN2 | fldln2 | 5, 3 |
FLDCW mem16 | fldcw ctrlword | 7 |
FLDENV mem | fldenv [bp+10] | 37, 16-bit pm=32, 32-bit pm=33 |
FMUL [reg,reg] | fmul st(5),st | 3, 1 |
FMULP reg,ST | fmulp st(6),st | 3, 1 |
FIMUL memint | fimul warray[di] | 7, 4 |
FNOP | fnop | 1 |
FPATAN | fpatan | 17-173 |
FPREM | fprem | 16-64 |
FPREM1 | fprem1 | 20-70 |
FPTAN | fptan | 17-173 |
FRNDINT | frndint | 9-20 |
FRSTOR mem | frstor [bp-94] | 16-bit rm or vm=75; 32-bit rm or vm=95; pm=70 |
FSAVE mem | fsave [bp-94] | 16-bit rm or vm=127+; 32-bit rm or vm=151+; pm=124+ |
FNSAVE mem | fnsave [bp-94] | 16-bit rm or vm=127; 32-bit rm or vm=151; pm=124 |
FSCALE | fscale | 20-31 |
FSIN | fsin | 16-126 |
FSINCOS | fsincos | 17-137 |
FSQRT | fsqrt | 70 |
FST reg | fst st | 1 |
FST memreal | fst longs[bx] | 2 |
FSTP reg | fstp st(3) | 1 |
FSTP mem32real | fstp longreal | 2 |
FSTP mem64real | Â |
2 |
FSTP mem80real | Â |
3 |
FSTCW mem16 | fstcw ctrlword | 2+ |
FNSTCW mem16 | fnstcw ctrlword | 2 |
FSTENV mem | fstenv [bp-14] | 16-bit rm or vm=50+; 32-bit rm or vm=48+; 16-bit pm=49+; 32-bit pm=50+ |
FNSTENV mem | fnstenv [bp-14] | 16-bit rm or vm=50; 32-bit rm or vm=48; 16-bit pm=49; 32-bit pm=50 |
FSTSW mem16 | fstsw statword | 2+ |
FSTSW AX | fstsw ax | 2+ |
FNSTSW mem16 | fnstsw statword | 2 |
FNSTSW AX | fnstsw ax | 2 |
FSUB [reg,reg] | fsub st,st(2) | 3, 1 |
FSUB memreal | fsub longreal | 3, 1 |
FSUBP reg,ST | fsubp st(6),st | 3, 1 |
FISUB memint | fisub double | 7, 4 |
FSUBR [reg,reg] | fsubr st,st(2) | 3, 1 |
FSUBR memreal | fsubr longreal | 3, 1 |
FSUBRP reg,ST | fsubrp st(6),st | 3, 1 |
FISUBR memint | fisubr double | 7, 4 |
FTST | ftst | 4, 1 |
FUCOM [reg] | fucom st(2) | 4, 1 |
FUCOMP [reg] | fucomp st(7) | 4, 1 |
FUCOMPP | fucompp | 4, 1 |
FWAIT | fwait | 1-3 |
FXAM | fxam | 21 |
FXCH [reg] | fxchg st(3) | 1 |
FXTRACT | fxtract | 13 |
FYL2X | fyl2x | 22-111 |
FYL2XP1 | fyl2xp1 | 22-103 |
Hints, o cómo acelerar
Lo primero es que, como ya hemos visto con los ciclos de las instrucciones, el volcado y carga de memoria es lento, con lo cual, la primera regla, es realizar las menores cargas y volcados posibles, aprovechando al maximo las posiciones de la pila para cuantas más operaciones mejor.
Por ejemplo, hemos de multiplicar un array por 5...
.DATA Array dd 200 dup(?) ;Array con datos.. :D Value dd 5 .CODE fild Dword Ptr ds:[Value] mov ecx,200 ;200 datos a multiplicar mov edi,offset Array Bucle: fild Dword Ptr ds:[edi] ;fild suponiendo que son ;enteros.. fmul ;Multiplica St(0),St(1) fistp Dword Ptr ds:[edi] ;descargamos el operando add edi,4 dec ecx jnz Bucle ffree St(0) ;Liberar el dato "5" :)
o bien, para ahorrarnos el ffree... :)
fild Dword Ptr ds:[Value] mov ecx,199 ;200 datos a multiplicar mov edi,offset Array Bucle: fild Dword Ptr ds:[edi] ;fild suponiendo que son ;enteros.. fmul ;Multiplica St(0),St(1) fistp Dword Ptr ds:[edi] ;descargamos el operando add edi,4 dec ecx jnz Bucle fmulp ;St(0)=St(0)*St(1) ;pop St(1) > nul fistp Dword Ptr ds:[edi]
Esto en realidad es un ejemplo tonto, pero como podemos observar la enseñanza del mismo es directamente aplicable a muchas rutinas matemáticas de ámbito 3D.
Otra cosa a tener en cuenta es que, si las cargas y descargas ya de por sà son lentas (hay que tener en cuenta cache hits en las cargas) es más lento todavÃa con la inclusión de la particula "i", ya que la FPU ha de convertir ese dato entero a real, esto provoca que siempre perdamos unos pocos ciclos en la operación. Aun asÃ, lo expuesto arriba ya de por sà es más rápido que lo mismo escrito en código de procesador (osea con imuls,etc).
Esto es fácilmente solucionable, si nuestra aplicacion necesita realizar diversos cálculos matemáticos con un array de vectores, con lo cual lo realmente óptimo es tener almacenados esos datos en real, operar con ellos, y en la ultima operación a realizar para su utilización (una proyección 3D a 2D, por ejemplo) aprovechar para hacer la conversión a entero.
También hay otras optimizaciones, hay algunas instrucciones pairebles, concretamente, el Fmul con el Fxchg, pero de esto hablaré en una sucesiva actualizacion de documento (¡maldito tiempo!).
Ejemplo Práctico: Una rutina de proyección 3D a 2D
Los arrays son de Dwords en formato X,Y,Z con 12 bytes cada vector. El de entrada es real, el de salida sera entero.
;In Ecx=Number of vertex ; Edi=pointer to destination ; Esi=pointer of 3D vertex ; ;Out Action Performed! ;--------------------------------------- ProyCords Proc Near fild ds:[Ycenter] ;World Centering on screen fild ds:[Xcenter] ;Idem. fild ds:[Pvi] ;Focal Distance. c3d2d: fld ds:[esi+8] ;Get "Z" fld1 ;Load number "1" fdivrp ;Calc Inverse and pop ;the not valuable operand fld Dword Ptr ds:[esi+4] ;Get "Y" fmul st,st(2) ;Multiply Y with Focal Distance fmul st,st(1) ;Multiply with the inverse ;of the "Z" fadd st,st(4) ;Add Y Screen centering fistp Dword Ptr ds:[edi+4] ;Store final value fld Dword Ptr ds:[esi] ;Get "X" fmul st,st(2) ;Multiply X with Focal Distance fmul st,st(1) ;Multiply with the inverse ;of the "Z" fadd st,st(3) ;Add X Screen centering fistp Dword Ptr ds:[edi] ;Store final value fstp ds:[Dummy] ;Free the Z inverse add esi,12 ;Increment pointers add edi,12 ;Increment pointers dec ecx ;decrement counter jnz c3d2d ;if not 0 go to other vertex fistp ds:[Dummy] fistp ds:[Dummy] fistp ds:[Dummy] ;Free all ret ProyCords Endp
Como se puede observar en esta rutina, hacemos uso de los dos ejemplos de optimizacion expuestos anteriormente. Tomar los datos, desde una base de datos de reales, utilizar la conversión 3D a 2D para pasarlos a enteros (para poder realizar luego el pintado) y utilizar al máximo de lo que se pueda las posiciones de la pila para guardar datos que han de ser reutilizados. Otra optimización es el uso de inversos, para lo cual primero calculamos ZI=1/Z y luego para obtener X'=X*PVI/Z realizamos la siguiente operación: X'=(X*PVI)*ZI.
Desde luego esto es más rápido que dos divisiones pero, de todas formas, aunque se realizara con dos divisiones de copro, el resultado sigue siendo todavÃa más rápido que su misma ecuación programada con el procesador.
Además, los inversos se pueden utilizar hasta en los sitios mas insospechados. Por ejemplo, en nuestro sistema de sonido, en el player de Sb hay una tabla de inversos para eliminar DIV's en la rutina de mezcla.
Cuidado con...
En primer lugar, mucho cuidado con la pila, cualquier desestabilizacion de la pila puede provocar resultados insospechados y hasta, en muchos casos, el cuelgue total del computador.
En segundo lugar, con los compiladores. Ignoro si hay algun compilador perfecto para el inline en asm, porque realmente yo no uso ninguno, siempre programo en 100% ensamblador, pero he visto casos como el de un chaval que una noche a través del IRC me comento que un bucle con MUL's y otro con FMUL's le funcionaban a la misma velocidad. Me paso el exe, y me di cuenta que el WATCOM C le habia generado en vez de FMUL's, FMULP's, con lo cual siempre popeaba datos y ocasionaba una excepción de pila, lo que hacia que el codigo de copro se ejecutara más lento. Las excepciones de pila por arriba, esto es por St(0) son controlables, pero por abajo (St(7)) no lo son tanto.
No sé si esto es un bug declarado del compilador, pero tambien he oido que existen algunos problemas con la conversion de real a entero de la citada Math387 del Watcom C, por lo cual aconsejo, que después de haber escrito la rutina, coger un debugger y asegurarse de que el codigo generado por el compilador es exacto.
Nosotros aqui en TLOTB trabajamos tanto con TASM como con NASM y con ninguno de los dos hemos tenido ningun problema con las instrucciones y sintaxis de copro, con lo cual tambien aconsejo el ensamblar las rutinas con alguno de estos dos ensambladores y despues enlazarlas con el programa principal.
¿Que hay de verdad sobre las transferencias con copro?
Bien, esto es un tema peliagudo, y lo evaluaremos en dos casos particulares.
- Memoria->Memoria.
En este caso se obtiene una ligera mejoria por tres razones. La primera es porque el procesador internamente esta orientado al trabajo con Qwords (si no, mirar la instruccion MOVQ del MMX).
La segunda es el conexionado del procesador a la memoria del sistema, cuya transmisión optima es 64bits (mejor si disponemos de modulos de memoria DIMM). La tercera, el alineamiento a 64bits es lo mas óptimo en una CPU Pentium. AquÃ, en transferencia, hay que tener cuidado con los NaN.
- Memoria->Bus (SVGA,etc)
En este caso depende mucho del chipset utilizado en la placa base. Despues de pruebas en varios equipos, se llega a la conclusion de que, aunque gastemos transferencia por copro, en el peor de los casos tarda lo mismo que con Dwords (osea un MOVSD) y en el mejor de los casos se gana en torno a un 5% o 10%. Esto oscila mucho dependiendo de la saturacion del BUS, de la SVGA y su velocidad para tragar datos,etc. Con lo cual, de todas formas es aconsejable su utilizacion, o al menos, probar que tal va! :)
Más Hints
Para borrar una zona de memoria (por ejemplo una pantalla virtual) el copro bate records.
Alineamiento... hacer la siguiente prueba...
- Tomar una direccion alineada por ejemplo a Qword
- esperar al retrazo
- colocar el color de borde a verde por ejemplo
- volcar la pantalla.
- colocar el color de borde a negro por ejemplo
- goto al principio
Apuntar lo que tarda y luego hacer lo mismo con una direccion sin alinear.. ¡veréis la grandiosa diferencia! :)
Palabras de control y estado de coprocesador y registros de flag
Aqui esta la especificación de bits de las palabras de estado y de control, Hay que tener en cuenta que esta documentación la he podido conseguir sólo del 387, y que hay bastantes diferencias con el 287, asi que podrÃa ser que en los actuales cambie alguna cosa.
PALABRA DE CONTROL
Bit | Nombre | Descripción |
---|---|---|
15-13 | -- | Reservados en el 387 |
12 | -- | Reservados en el 387; en el 287 era control de infinito. |
11-10 | RC | Rounding Control:
|
9-8 | PC | Precision Control:
|
7-6 | -- | Reservados en el 387 |
5 | PM | Máscara de excepción de Precisión |
4 | UM | Máscara de excepción de Underflow |
3 | OM | Máscara de excepción de Overflow |
2 | ZM | Máscara de excepción de Zero Divide |
1 | DM | Máscara de excepción de Denormalized Operand |
9 | IM | Máscara de excepción de Invalid Operation |
PALABRA DE ESTADO
Bit | Nombre | Descripción |
---|---|---|
15 | B | Flag de Busy |
14 | C3(Z) | Parte del Condition Code. |
13-11 | TOP | Puntero de pila (Top of Stack) |
10 | C2(C) | Parte del Condition Code. |
9 | C1(A) | Parte del Condition Code. |
8 | C0(S) | Parte del Condition Code. |
7 | ES | Error Summary flag |
6 | SF | Stack Flag |
5 | PE | Excepción de Precision |
4 | UE | Excepción de Underflow |
3 | OE | Excepción de Overflow |
2 | ZE | Excepción de Zero Divide |
1 | DE | Excepción de Denormalized Operand |
0 | IE | Excepción de Invalid Operation |
Los Condition Codes son bastante complicados de interpretar, ya que su interpretación depende de la instrucción. Baste decir que tras la comparación (FCOM y familia) resulta lo siguiente:
C3 | C2 | C0 | Significado |
---|---|---|---|
0 | 0 | 0 | TOP > Operand |
0 | 0 | 1 | TOP < Operand |
1 | 0 | 0 | TOP = Operand |
1 | 1 | 1 | Unordered |
C1 es para la comparacion el flag de Zero, aunque no sé si eso significa que debe ser interpretado igual que el flag de Zero del registro Flags.
Si hay un Overflow o Underflow de pila, los bits IE y SF se pondrán a 1, y el flag C1 distinguira entre overflow (1) y underflow (0).
Denormals
(Esto no está en la documentacion de intel, y puedo equivocarme en algo).
La representación en coma flotante consiste en un bit de signo, un exponente y una mantisa, como sabéis. Para permitir exponentes negativos, en vez de almacenar el exponente en formato de complemento a dos, se almacena sumándole una cantidad llamada Bias, que es 127 para el Single, 1023 para el Double y 16383 para el Extended.
Los numeros Single y Double suponen un 1,... implÃcito antes de empezar la mantisa, pero en el Extended no es implÃcito, sino que lo necesita explicitamente.
Parece una tonterÃa, pero en los Single y Double, ¿cómo se almacena un cero? Porque el siguiente caso, que se corresponde a todos los bits a cero (pongamos que de Single):
- Exponente = 0
- Mantisa = (1,)000000000000...
- Signo = 0
significaria 1,0000... * 2^-127, pero no cero (aunque esté cerca).
La solución que se ha adoptado es considerar como caso especial el de Exponente = 0, en cuyo ese caso en vez de haber un 1 implÃcito en la mantisa hay un 0 implÃcito.
Asi:
- Exponente = 0
- Mantisa = (0,)0001001110001000
- Signo = 0
se corresponde a 0,0001001110001000 * 2^-127, o lo que es igual, 1,001110001000 * 2^-131.
Ademas, de esa manera el cero puede ser representado, ya que 0,00000000000... * 2^-127 = 0.
A toda la familia de numeros con Exponente = 0, excepto el cero, se les llama Denormals. En el libro de Knuth (Seminumerical Algorithms) menciona la utilidad de los Denormals para no perder tanta precisión en ciertos cálculos.
El caso de los Extended no es diferente, salvo que el 1 explicito puede dejar de estar ahi cuando el exponente es cero (en el resto de numeros, el 1 era obligatorio que estuviera).
Ahora una nota del cuadernillo:
"FLD single/double cuando el operando es un Denormal convierte el numero a Extended precision y lanza la excepcion de Denormalized Operand. Al cargar un Signaling NaN, FLD single/double lanza una excepcion de Invalid Operand."
NaN significa Not a Number, aunque no sé qué formato tienen esos.
La tabla de excepciones dice lo siguiente:
Excepción | Causa | Acción por defecto (si la excepción está enmascarada) |
---|---|---|
Invalid Op. | Operación sobre un Signaling NaN, formato no soportado, operacion indeterminada (0*infinito, 0/0, infinito-infinito, etc), o underflow/overflow de pila | El resultado es un Quiet NaN, un entero indefinido o un BCD indefinido. |
Denormalized Op. | Al menos uno de los operandos es un Denormal, o sea, tiene el menor exponente pero una mantisa no-cero. | Continua el proceso normal. |
Zero Divisor | El divisor es cero mientras el dividendo es un numero no cero y no infinito. | El resultado es Infinito. |
Overflow | El resultado es demasiado grande para caber en el formato especificado. | El resultado es el mayor numero posible o infinito. |
Underflow | El resultado es no-cero pero es demasiado pequeño para caber en el formato especificado, y si está enmascarada la excepción de Underflow, la denormalizacion causa pérdida de precisión. | El resultado es un denormal o cero. |
Resultado Inexacto (Precisión) | El resultado EXACTO no es representable en el formato especificado (p.ej. 1/3); el resultado es redondeado de acuerdo con el modo de redondeo. | Continua el proceso normal. |
Para terminar.... de empezar.... :))
Lo aconsejable, es realizar al principio del programa un finit para inicializar el copro, despues descargar la palabra de control y enmascarar las excepciones y luego volver a cargarlas, con lo cual, reduciremos el tiempo de proceso de las instrucciones que puedan causar una excepcion.
Espero que esto os ayude en algo, la verdad para mi es suficiente para lo que es el manejo habitual de copro.
Agradecimientos y Saludos
Ummm.... a.....
- Cranky/TloTb.. por su apoyo linguistico, y por documentacion.
- Unreal/Pulse.. por incitarme a escribir esto.
- A los habituales en #demos,#coders y en es.comp.demos
Saludos especiales para Jcab/Iguana por la charla que tuvimos sobre los Denormals, los NaN y las transferencias de copro.
Todas las marcas registradas son propiedad de sus respectivos dueños, etc.. :)
Si alguien quiere una mejor revisión de este documento, o simplemente quiere consultarme algo o bien mandarme una amenaza para que deje de escribir...
Podeis dar conmigo en [email protected] o a traves de http://www.tlotb.com.