Está en la página 1de 24

Guía de computación numérica

Apéndice d
Lo que todo científico informático debe saber sobre la aritmética de punto
flotante
Nota: este apéndice es una reedición editada del artículo Lo que todo científico informático debería saber sobre la aritmética de punto flotante , por David Goldberg, publicado
en marzo de 1991 en Computing Surveys. Copyright 1991, Association for Computing Machinery, Inc., reimpreso con permiso.

Resumen
La aritmética de punto flotante es considerada un tema esotérico por muchas personas. Esto es bastante sorprendente porque el punto flotante es ubicuo en los sistemas
informáticos. Casi todos los idiomas tienen un tipo de datos de punto flotante; las computadoras desde PC a supercomputadoras tienen aceleradores de punto flotante; la
mayoría de los compiladores serán llamados para compilar algoritmos de punto flotante de vez en cuando; y prácticamente todos los sistemas operativos deben responder a
excepciones de punto flotante, como el desbordamiento. Este artículo presenta un tutorial sobre aquellos aspectos del punto flotante que tienen un impacto directo en los
diseñadores de sistemas informáticos. Comienza con el fondo en la representación de punto flotante y el error de redondeo, continúa con una discusión del estándar de punto
flotante IEEE y concluye con numerosos ejemplos de cómo los desarrolladores de equipos pueden admitir mejor el punto flotante.

Categorías y descriptores de materias: (Primario) C.0 [Organización de sistemas de computación]: General - diseño de conjunto de instrucciones ; D.3.4 [Lenguajes de
programación]: procesadores - compiladores, optimización ; G.1.0 [Análisis Numérico]: General - aritmética computacional, análisis de errores, algoritmos numéricos
(Secundarios)

D.2.1 [Ingeniería de software]: Requisitos / especificaciones - idiomas ; D.3.4 Lenguajes de programación]: Definiciones formales y teoría - semántica ; D.4.1 Sistemas
Operativos]: Gestión de Procesos - sincronización .

Términos Generales: Algoritmos, Diseño, Idiomas.

Palabras y frases clave adicionales: Número desnormalizado, excepción, punto flotante, estándar de punto flotante, flujo insuficiente gradual, dígito de guarda, NaN,
desbordamiento, error relativo, error de redondeo, modo de redondeo, ulp, flujo insuficiente.

Introducción
Los constructores de sistemas informáticos a menudo necesitan información sobre la aritmética de punto flotante. Sin embargo, hay muy pocas fuentes de información
detallada al respecto. Uno de los pocos libros sobre el tema, Floating-Point Computation de Pat Sterbenz, hace tiempo que está agotado. Este artículo es un tutorial sobre
aquellos aspectos de la aritmética de punto flotante ( punto flotante en adelante) que tienen una conexión directa con la construcción de sistemas. Se compone de tres partes
flojamente conectadas. La primera sección, Error de redondeo , analiza las implicaciones de usar diferentes estrategias de redondeo para las operaciones básicas de suma,
resta, multiplicación y división. También contiene información básica sobre los dos métodos para medir el error de redondeo, las ulps yrelative error. La segunda parte analiza
el estándar de punto flotante IEEE, que está siendo aceptado rápidamente por los fabricantes comerciales de hardware. Incluido en el estándar IEEE está el método de
redondeo para operaciones básicas. La discusión de la norma se basa en el material en la sección Error de redondeo . La tercera parte discute las conexiones entre el punto
flotante y el diseño de varios aspectos de los sistemas informáticos. Los temas incluyen el diseño de conjuntos de instrucciones, la optimización de compiladores y el manejo
de excepciones.

He tratado de evitar hacer afirmaciones sobre el punto flotante sin dar razones por las cuales las afirmaciones son ciertas, especialmente porque las justificaciones no
involucran nada más complicado que el cálculo elemental. Esas explicaciones que no son centrales para el argumento principal se han agrupado en una sección llamada "Los
detalles", de modo que se pueden omitir si se desea. En particular, las pruebas de muchos de los teoremas aparecen en esta sección. El final de cada prueba está marcado
con el símbolo z . Cuando no se incluye una prueba, la z aparece inmediatamente después de la declaración del teorema.

Error de redondeo
Exprimir infinitos números reales en un número finito de bits requiere una representación aproximada. Aunque hay infinitos enteros, en la mayoría de los programas el
resultado de los cálculos de enteros se puede almacenar en 32 bits. En contraste, dado un número fijo de bits, la mayoría de los cálculos con números reales producirán
cantidades que no se pueden representar exactamente con tantos bits. Por lo tanto, el resultado de un cálculo de punto flotante a menudo debe redondearse para encajar en
su representación finita. Este error de redondeo es el rasgo característico del cálculo de punto flotante. La sección Relative Error and Ulps describe cómo se mide.

Como la mayoría de los cálculos de punto flotante tienen un error de redondeo, ¿importa si las operaciones aritméticas básicas introducen un poco más de error de redondeo
de lo necesario? Esa pregunta es un tema principal a lo largo de esta sección. La sección Guard Digits analiza los dígitos de la guarda , un medio para reducir el error al restar
dos números cercanos. IBM consideró que los dígitos de guarda eran lo suficientemente importantes como para que en 1968 agregara un dígito de guarda al formato de doble
precisión en la arquitectura del Sistema / 360 (la precisión simple ya tenía un dígito de guarda), y modificó todas las máquinas existentes en el campo. Se dan dos ejemplos
para ilustrar la utilidad de los dígitos de guarda.

El estándar IEEE va más allá de simplemente requerir el uso de un dígito de guarda. Proporciona un algoritmo para la suma, resta, multiplicación, división y raíz cuadrada, y
requiere que las implementaciones produzcan el mismo resultado que ese algoritmo. Por lo tanto, cuando un programa se mueve de una máquina a otra, los resultados de las
operaciones básicas serán los mismos en cada bit si ambas máquinas admiten el estándar IEEE. Esto simplifica enormemente la portabilidad de los programas. Otros usos de
esta especificación precisa se dan en Operaciones Exactamente Redondeadas .

Formatos de punto flotante


1
Se han propuesto varias representaciones diferentes de números reales, pero, con mucho, la más utilizada es la representación de punto flotante. Las representaciones de
-1
punto flotante tienen una base (que siempre se supone que es uniforme) y una precisión p . Si = 10 y p = 3, entonces el número 0.1 se representa como 1.00 × 10 . Si
-4
= 2 yp = 24, entonces el número decimal 0.1 no se puede representar exactamente, pero es aproximadamente 1.10011001100110011001101 × 2 .

2
En general, un número de punto flotante se representará como ± d.dd ... d × e , donde d.dd ... d se denomina significando y tiene p dígitos. Más precisamente ± d 0 . d 1 d 2
... d p-1 × e representa el número

(1) .

El término número de punto flotante se utilizará para significar un número real que puede representarse exactamente en el formato en discusión. Otros dos parámetros
p
asociados con las representaciones de punto flotante son los exponentes permitidos más grandes y más pequeños, e ye . Dado que hay posibles significados, y e
max min
max
- e min + 1 exponentes posibles, un número de punto flotante puede codificarse en

bits, donde el +1 final es para el bit de signo. La codificación precisa no es importante por ahora.

Hay dos razones por las que un número real podría no ser exactamente representable como un número de punto flotante. La situación más común se ilustra con el número
decimal 0.1. Aunque tiene una representación decimal finita, en binario tiene una representación de repetición infinita. Por lo tanto, cuando = 2, el número 0.1 se encuentra
estrictamente entre dos números de punto flotante y ninguno de ellos puede representarlos exactamente. Una situación menos común es que un número real está fuera de
rango, es decir, su valor absoluto es mayor que × o menor que 1.0 × . La mayor parte de este documento discute problemas debido a la primera razón. Sin embargo, los
números que están fuera de rango se tratarán en las secciones Infinito y Números desnormalizados .

1 -1
Las representaciones de punto flotante no son necesariamente únicas. Por ejemplo, tanto 0.01 × 10 como 1.00 × 10 representan 0.1. Si el dígito inicial es distinto de
-1
cero ( d 0 en la ecuación (1) anterior), entonces se dice que la representación está normalizada . El número de punto flotante 1.00 × 10 está normalizado, mientras que
0
1
0.01 × 10 no lo está. Cuando = 2, p = 3, e = -1 y e = 2 hay 16 números de punto flotante normalizados, como se muestra enFIGURA D-1 . Las marcas de control
min max
en negrita corresponden a números cuyo significado es 1.00. Requerir que una representación de punto flotante se normalice hace que la representación sea única.
Desafortunadamente, esta restricción hace que sea imposible representar cero! Una forma natural de representar 0 es con 1.0 × , ya que esto preserva el hecho de que
3
el ordenamiento numérico de los números reales no negativos corresponde al ordenamiento lexicográfico de sus representaciones de punto flotante. Cuando el exponente se
almacena en un campo de k bits, eso significa que solo hay 2 k - 1 valores disponibles para su uso como exponentes, ya que uno debe estar reservado para representar 0.

Tenga en cuenta que la × en un número de punto flotante es parte de la notación y es diferente de una operación de multiplicación de punto flotante. El significado del
-3 2
símbolo × debe quedar claro en el contexto. Por ejemplo, la expresión (2.5 × 10 ) × (4.0 × 10 ) implica solo una multiplicación de punto flotante.

FIGURA D-1 Números normalizados cuando = 2, p = 3, e = -1, e =2


min max

Error relativo y Ulps

Dado que el error de redondeo es inherente al cálculo de punto flotante, es importante tener una forma de medir este error. Considere el formato de punto flotante con = 10
-2
y p = 3, que se utilizará en esta sección. Si el resultado de un cálculo de punto flotante es 3.12 × 10 , y la respuesta cuando se calcula con una precisión infinita es .0314,
-2
está claro que esto es un error de 2 unidades en el último lugar. De manera similar, si el número real .0314159 se representa como 3.14 × 10 , entonces está en error por
.159 unidades en el último lugar. En general, si el número de coma flotante dd ... d × se usa para representar z , entonces está en error por dd ... d - ( z / e ) unidades p-1
e
4,5
en el último lugar. El término ulps se usará como una abreviatura de "unidades en el último lugar". Si el resultado de un cálculo es el número de punto flotante más
cercano al resultado correcto, aún podría tener un error de hasta .5 ulp. Otra forma de medir la diferencia entre un número de punto flotante y el número real que se está
aproximando es el error relativo , que es simplemente la diferencia entre los dos números divididos por el número real. Por ejemplo, el error relativo cometido al aproximar
0
3.14159 por 3.14 × 10 es .00159 / 3.14159 .0005.

Para calcular el error relativo que corresponde a .5 ulp, observe que cuando un número real se aproxima por el número de punto flotante más cercano posible d.dd ... dd × e ,
el error puede ser tan grande como 0.00 ... 00 ' × e , donde ' es el dígito / 2, hay p unidades en el significado del número de coma flotante y p unidades de 0 en el significado
del error. Este error es (( / 2) -p ) × e . Dado que los números de la forma d.dd ... dd × e todos tienen el mismo error absoluto, pero tienen valores que oscilan
e e
entre y × , los rangos de error relativos entre (( / 2) -p e e
) × / y (( / 2) -p )× / e e + 1 . Es decir,

(2)

En particular, el error relativo correspondiente a 0.5 ulp puede variar por un factor de . Este factor se llama el bamboleo . Configurando = ( / 2) -p al mayor de los límites
en (2) arriba, podemos decir que cuando un número real se redondea al número de punto flotante más cercano, el error relativo siempre está delimitado por e , que se refiere
Como máquina epsilon .

En el ejemplo anterior, el error relativo fue .00159 / 3.14159 .0005. Para evitar números tan pequeños, el error relativo normalmente se escribe como un factor
-3
multiplicador , que en este caso es =( / 2) -p = 5 (10) = .005. Así, el error relativo se expresaría como (.00159 / 3.14159) /. 005) 0.1 .

1
Para ilustrar la diferencia entre ulps y error relativo, considere el número real x = 12.35. Se aproxima por = 1.24 × 10 . El error es 0.5 ulps, el error relativo es 0. 8 .
1
Consideremos a continuación el cálculo 8 . El valor exacto es 8 x = 98.8, mientras que el valor calculado es 8 = 9.92 × 10 . El error es ahora 4.0 ULPs, pero el error
relativo es todavía 0. 8 . El error medido en ulps es 8 veces mayor, aunque el error relativo es el mismo. En general, cuando la base es , un error relativo fijo expresado en
ulps puede oscilar por un factor de hasta . Y a la inversa, como lo muestra la ecuación (2) anterior, un error fijo de .5 ulps da como resultado un error relativo que puede
tambalearse .

La forma más natural de medir el error de redondeo es en ulps. Por ejemplo, redondear al número de punto flotante más cercano corresponde a un error menor o igual a .5
ulp. Sin embargo, al analizar el error de redondeo causado por varias fórmulas, el error relativo es una mejor medida. Un buen ejemplo de esto es el análisis de la sección
Teorema 9 . Dado que se puede sobrestimar el efecto del redondeo al número de punto flotante más cercano por el factor de oscilación , las estimaciones de error de las
fórmulas serán más estrictas en las máquinas con una pequeña .

Cuando solo el orden de magnitud del error de redondeo es de interés, las ulps y se pueden usar indistintamente, ya que difieren a lo sumo en un factor de . Por ejemplo,
cuando un número de punto flotante tiene un error por n ulps, eso significa que el número de dígitos contaminados es log n . Si el error relativo en un cálculo es n , entonces

(3) dígitos contaminados log n .

Dígitos de la guardia

Un método para calcular la diferencia entre dos números de punto flotante es calcular la diferencia exactamente y luego redondearla al número de punto flotante más
12 -5
cercano. Esto es muy caro si los operandos difieren mucho en tamaño. Suponiendo que p = 3, 2.15 × 10 - 1.25 × 10 se calcularía como

12
x = 2,15 × 10
12
y = .0000000000000000125 × 10
12
x - y = 2.1499999999999999875 × 10

12
que se redondea a 2,15 × 10 . En lugar de usar todos estos dígitos, el hardware de punto flotante normalmente opera con un número fijo de dígitos. Supongamos que el
número de dígitos guardados es p , y que cuando el operando más pequeño se desplaza hacia la derecha, los dígitos simplemente se descartan (en lugar de redondear).
12 -5 se
Entonces 2.15 × 10 - 1.25 × 10 convierte en

12
x = 2.15 × 10
12
y = 0.00 × 10
12
x - y = 2.15 × 10

La respuesta es exactamente la misma que si la diferencia hubiera sido calculada exactamente y luego redondeada. Tomemos otro ejemplo: 10.1 - 9.93. Esto se convierte en
1
x = 1.01 × 10
1
y = 0.99 × 10
1
x - y = .02 × 10

La respuesta correcta es .17, por lo que la diferencia calculada está desactivada en 30 ulps y está mal en cada dígito. ¿Qué tan malo puede ser el error?

Teorema 1

Usando un formato de punto flotante con parámetros y p , y calculando las diferencias usando los dígitos p , el error relativo del resultado puede ser tan grande como - 1 .

Prueba

Un error relativo de - 1 en la expresión x - y ocurre cuando x = 1.00 ... 0 y y = . ... , donde = - 1. Aquí y tiene p dígitos (todos iguales a ). La diferencia exacta es x
-p - p +1
-y= . Sin embargo, al calcular la respuesta utilizando solo p dígitos, el dígito más a la derecha de y se desplaza, por lo que la diferencia calculada es . Así el error es
-p - +1 -p -p -p
- p = ( - 1), y el error relativo es ( - 1) / = - 1. z

Cuando = 2, el error relativo puede ser tan grande como el resultado, y cuando = 10, puede ser 9 veces más grande. O para decirlo de otra manera, cuando = 2, la
ecuación (3) muestra que el número de dígitos contaminados es log (1 / ) = log (2 p ) = p . Es decir, todos los dígitos p en el resultado son incorrectos! Supongamos que
2 2
se agrega un dígito adicional para protegerse contra esta situación (un dígito de guarda ). Es decir, el número más pequeño se trunca a p + 1 dígitos, y luego el resultado de
la resta se redondea a p dígitos. Con un dígito de guarda, el ejemplo anterior se convierte en

1
x = 1.01 0 × 10
1
y = 0.993 × 10
1
x - y = .017 × 10

y la respuesta es exacta. Con un solo dígito de guarda, el error relativo del resultado puede ser mayor que , como en 110 - 8.59.
2
x = 1.1 0 × 10
2
y = .085 × 10
2
x - y = 1.015 × 10

Esto se redondea a 102, en comparación con la respuesta correcta de 101.41, para un error relativo de .006, que es mayor que = .005. En general, el error relativo del
resultado puede ser solo un poco mayor que . Más precisamente,

Teorema 2

Si x e y son números de punto flotante en un formato con parámetros y p , y si la resta se realiza con p + 1 dígitos (es decir, un dígito de guarda), entonces el error de
redondeo relativo en el resultado es menor que 2 .

Este teorema será probado en el error de redondeo . La adición se incluye en el teorema anterior, ya que x e y pueden ser positivos o negativos.

Cancelación

La última sección se puede resumir diciendo que sin un dígito de guarda, el error relativo cometido al restar dos cantidades cercanas puede ser muy grande. En otras
palabras, la evaluación de cualquier expresión que contenga una resta (o una suma de cantidades con signos opuestos) podría resultar en un error relativo tan grande que
todos los dígitos carecen de significado (Teorema 1). Al restar cantidades cercanas, los dígitos más significativos en los operandos coinciden y se cancelan entre sí. Hay dos
tipos de cancelación: catastrófica y benigna.
2
La cancelación catastrófica se produce cuando los operandos están sujetos a errores de redondeo. Por ejemplo, en la fórmula cuadrática, aparece la expresión b - 4 ac . Las
2
cantidades b y 4 ac están sujetas a errores de redondeo, ya que son el resultado de multiplicaciones de punto flotante. Supongamos que se redondean al número de punto
flotante más cercano, por lo que son precisos dentro de 0.5 ulp. Cuando se restan, la cancelación puede hacer que desaparezcan muchos de los dígitos exactos, dejando atrás
principalmente los dígitos contaminados por un error de redondeo. De ahí que la diferencia pueda tener un error de muchas ulps. Por ejemplo, considere b = 3.34, a = 1.22, y
2 2 se
c = 2.28. El valor exacto de b - 4 ac es .0292. Pero b redondea a 11.2 y 4 ac se redondea a 11.1, por lo tanto, la respuesta final es .1, que es un error de 70 ulps,
6
aunque 11.2 - 11.1 es exactamente igual a .1 . La resta no introdujo ningún error, sino que expuso el error introducido en las multiplicaciones anteriores.

La cancelación benigna se produce al restar cantidades exactamente conocidas. Si x e y no tienen error de redondeo, entonces por el teorema 2 si la resta se realiza con un
dígito de guarda, la diferencia x -y tiene un error relativo muy pequeño (menos de 2 ).

Una fórmula que muestra una cancelación catastrófica a veces se puede reorganizar para eliminar el problema. Nuevamente consideremos la fórmula cuadrática.

(4)

Cuando , entonces no implica una cancelación y

Pero la otra adición (resta) en una de las fórmulas tendrá una cancelación catastrófica. Para evitar esto, multiplica el numerador y el denominador de r por
1

(y de manera similar para r ) para obtener


2

(5)

Si y , luego el cálculo de r utilizando la fórmula (4) implicará una cancelación. Por lo tanto, use la fórmula (5) para calcular r y (4) para r . Por otro lado, si b <0,
1 1 2
use (4) para calcular r y (5) para r .
1 2

2 2 7
La expresión x - y es otra fórmula que presenta una cancelación catastrófica. Es más preciso evaluarlo como ( x - y ) ( x + y ). A diferencia de la fórmula cuadrática, esta
forma mejorada todavía tiene una resta, pero es una cancelación benigna de cantidades sin error de redondeo, no catastrófica. Por el Teorema 2, el error relativo en x - y es
como máximo 2 . Lo mismo ocurre con x + y . Al multiplicar dos cantidades con un error relativo pequeño, se obtiene un producto con un error relativo pequeño (consulte la
secciónError de redondeo ).

Para evitar confusiones entre los valores exactos y computados, se utiliza la siguiente notación. Mientras que x - y denota la diferencia exacta de x e y , x y denota la
diferencia calculada (es decir, con un error de redondeo). De manera similar , y denota suma, multiplicación y división computadas, respectivamente. Todas las
mayúsculas indican el valor calculado de una función, como en LN(x)o SQRT(x). Las funciones en minúscula y la notación matemática tradicional denotan sus valores exactos
como en ln ( x ) y .

2 2
A pesar de que ( x y ) ( x y ) es una excelente aproximación a x - y , los números de punto flotante x e y pueden ser ellas mismas aproximaciones a algunas cantidades
reales y . Por ejemplo, y podrían ser números decimales exactamente conocidos que no pueden expresarse exactamente en binario. En este caso, aunque x y es una buena
2 2
aproximación a x - y , puede tener un error relativo enorme en comparación con la expresión verdadera , por lo que la ventaja de ( x + y ) ( x - y ) sobre x -y
2 2
no es tan dramático. Dado que computar ( x + y ) ( x - y ) es aproximadamente la misma cantidad de trabajo que computar x - y , es claramente la forma preferida en
este caso. En general, sin embargo, reemplazar una cancelación catastrófica por una benigna no vale la pena si el gasto es grande, porque la entrada es a menudo (pero no
siempre) una aproximación. Pero eliminar una cancelación por completo (como en la fórmula cuadrática) vale la pena incluso si los datos no son exactos. A lo largo de este
documento, se supondrá que las entradas de coma flotante a un algoritmo son exactas y que los resultados se calculan con la mayor precisión posible.

2 2
La expresión x - y es más precisa cuando se reescribe como ( x - y ) ( x + y ) porque una cancelación catastrófica se reemplaza por una benigna. A continuación
presentamos ejemplos más interesantes de fórmulas que muestran una cancelación catastrófica que se pueden reescribir para mostrar solo una cancelación benigna.

El área de un triángulo se puede expresar directamente en términos de las longitudes de sus lados a , b y c como

(6)

(Suponga que el triángulo es muy plano; es decir, a b + c . Luego s a , y el término ( s - a ) en la fórmula (6) resta dos números cercanos, uno de los cuales puede tener un
error de redondeo. Por ejemplo, si a = 9.0, b = c = 4.53, el valor correcto de s es 9.03 y A es 2.342 ... Aunque el valor calculado de s (9.05) tiene un error de solo 2 ulps, el
valor calculado de A es 3.04 , un error de 70 ulps.

Hay una forma de volver a escribir la fórmula (6) para que devuelva resultados precisos incluso para triángulos planos [Kahan 1986]. Es

(7)

Si un , b, y c no satisfacen un b c , cambiar su nombre antes de la aplicación (7) . Es sencillo comprobar que los lados derechos de (6) y (7) son algebraicamente idénticos. El
uso de los valores de a , b y c anteriores da un área calculada de 2.35, que es 1 ulp en error y mucho más precisa que la primera fórmula.

Aunque la fórmula (7) es mucho más precisa que (6) para este ejemplo, sería bueno saber qué tan bien (7) se desempeña en general.

Teorema 3

El error de redondeo incurrido al usar (7) para calcular el área de un triángulo es a lo sumo 11 , siempre que la resta se realice con un dígito de guarda, e .005, y que las
raíces cuadradas se calculen dentro de 1/2 ulp .

La condición de que e <.005 se cumpla en prácticamente todos los sistemas de punto flotante reales. Por ejemplo, cuando = 2, p 8 asegura que e <.005, y cuando = 10,
p 3 es suficiente.
En declaraciones como el teorema 3 que analizan el error relativo de una expresión, se entiende que la expresión se calcula utilizando aritmética de punto flotante. En
particular, el error relativo es en realidad de la expresión

(8) SQRT(( a ( b c )) ( c ( a b)) ( c ( a b )) (a ( b c ))) 4

Debido a la naturaleza incómoda de (8) , en la declaración de teoremas generalmente diremos el valor calculado de E en lugar de escribir E con notación circular.

Los límites de error suelen ser demasiado pesimistas. En el ejemplo numérico dado anteriormente, el valor computado de (7) es 2.35, comparado con un valor verdadero de
2.34216 para un error relativo de 0.7 , que es mucho menor que 11 . La razón principal para calcular los límites de error no es obtener límites precisos, sino verificar que la
fórmula no contiene problemas numéricos.

Un último ejemplo de una expresión que puede reescribirse para usar la cancelación benigna es (1 + x ) n , donde . Esta expresión surge en los cálculos financieros.
Considere depositar $ 100 todos los días en una cuenta bancaria que obtenga una tasa de interés anual del 6%, compuesta diariamente. Si n = 365 ei = .06, la cantidad de
dinero acumulado al final de un año es

100

dolares Si esto se calcula utilizando = 2 yp = 24, el resultado es $ 37615.45 en comparación con la respuesta exacta de $ 37614.05, una discrepancia de $ 1.40. La razón
del problema es fácil de ver. La expresión 1 + i / n implica agregar 1 a .0001643836, por lo que los bits de orden inferior de i / n se pierden. Este error de redondeo se
amplifica cuando 1 + i / n se eleva a la potencia n .

nln (1 + / )
La expresión problemática (1 + i / n ) n puede reescribirse como e i n , donde ahora el problema es calcular ln (1 + x ) para una pequeña x . Un enfoque es utilizar la
aproximación ln (1 + x ) x , en cuyo caso el pago se convierte en $ 37617.26, que está apagado en $ 3.21 e incluso menos preciso que la fórmula obvia. Pero hay una manera
de calcular ln (1 + x ) con mucha precisión, como muestra el Teorema 4 [Hewlett-Packard 1982]. ¡Esta fórmula produce $ 37614.07, con una precisión de dos centavos!

El teorema 4 supone que se LN(x)aproxima a ln ( x ) a 1/2 ulp. El problema que resuelve es que cuando x es pequeño, LN(1 x ) no está cerca de ln (1 + x ) porque 1 x ha
perdido la información en los bits de orden inferior de x . Es decir, el valor calculado de ln (1 + x ) no está cerca de su valor real cuando .

Teorema 4

Si ln (1 + x) se calcula utilizando la fórmula

el error relativo es a lo sumo 5 cuando 0 x <3/4, siempre que la resta se realice con un dígito de guarda, e <0.1, y se calcule ln dentro de 1/2 ulp.

Esta fórmula funcionará para cualquier valor de x, pero solo es interesante , que es donde se produce una cancelación catastrófica en la fórmula ingenua ln (1 + x ).
Aunque la fórmula puede parecer misteriosa, hay una explicación simple de por qué funciona. Escribe ln (1 + x ) como

El factor de la mano izquierda se puede calcular exactamente, pero el factor de la mano derecha µ ( x ) = ln (1 + x ) / x sufrirá un gran error de redondeo al agregar 1 a x .
Sin embargo, µ es casi constante, ya que ln (1 + x ) x . Así que cambiar x ligeramente no introducirá mucho error. En otras palabras, si , la computación será una buena
aproximación a x µ ( x ) = ln (1 + x ). ¿Hay un valor para el cual y se puede calcular con precisión? Ahi esta; a saber = (1 x ) 1, porque entonces 1 +
es exactamente igual a 1 x .

Los resultados de esta sección se pueden resumir diciendo que un dígito de guarda garantiza la precisión cuando se restan cantidades cercanas y precisamente conocidas
(cancelación benigna). A veces, una fórmula que da resultados inexactos se puede reescribir para tener una precisión numérica mucho mayor mediante el uso de una
cancelación benigna; sin embargo, el procedimiento solo funciona si la resta se realiza utilizando un dígito de guarda. El precio de un dígito de guarda no es alto, ya que
simplemente requiere que el sumador sea un poco más ancho. Para un sumador de doble precisión de 54 bits, el costo adicional es inferior al 2%. Por este precio, obtiene la
capacidad de ejecutar muchos algoritmos como la fórmula (6) para calcular el área de un triángulo y la expresión ln (1 + x). Aunque la mayoría de las computadoras
modernas tienen un dígito de guarda, hay algunas (como los sistemas Cray) que no las tienen.

Operaciones exactamente redondeadas

Cuando las operaciones de punto flotante se realizan con un dígito de guarda, no son tan exactas como si se hubieran calculado exactamente y luego se redondearon al
8
número de punto flotante más cercano. Las operaciones realizadas de esta manera serán llamadas exactamente redondeadas . El ejemplo inmediatamente anterior al
Teorema 2 muestra que un solo dígito de guarda no siempre dará resultados redondeados exactamente. La sección anterior dio varios ejemplos de algoritmos que requieren
un dígito de guarda para funcionar correctamente. Esta sección proporciona ejemplos de algoritmos que requieren un redondeo exacto.

Hasta el momento, la definición de redondeo no se ha dado. El redondeo es sencillo, con la excepción de cómo redondear los casos a medio camino; por ejemplo, ¿debería
12.5 redondear a 12 o 13? Una escuela de pensamiento divide los 10 dígitos por la mitad, permitiendo que {0, 1, 2, 3, 4} redondee hacia abajo y {5, 6, 7, 8, 9} redondeando
hacia arriba; por lo tanto, 12.5 redondearía a 13. Así es como funciona el redondeo en las computadoras VAX de Digital Equipment Corporation. Otra escuela de pensamiento
dice que dado que los números que terminan en 5 están a medio camino entre dos redondeos posibles, deberían redondear la mitad del tiempo y redondear la otra mitad. Una
forma de obtener este comportamiento del 50% es exigir que el resultado redondeado tenga su dígito menos significativo sea par. Así, 12.5 redondea a 12 en lugar de 13
porque 2 es par. ¿Cuál de estos métodos es mejor, redondear hacia arriba o redondear a la par Reiser y Knuth [1975] ofrecen la siguiente razón para preferir la ronda al par.

Teorema 5

Sean xey los números de punto flotante y defina x = x, x = (x y) y, ... , x n = (x n-1 y) y. Si y se redondean exactamente usando redondeo a par, entonces x n = x para
0 1 0
todos n o x n = x 1 para todos n 1. z

Para aclarar este resultado, considera = 10, p = 3 y deja x = 1.00, y = -.555. Al redondear, la secuencia se convierte en

x y = 1.56, x = 1.56 .555 = 1.01, x y = 1.01 .555 = 1.57,


0 1 1

9
y cada valor sucesivo de x aumenta en .01, hasta que x = 9.45 (n 845) . Debajo de la ronda para igualar, x es siempre 1.00. Este ejemplo sugiere que cuando se usa
n n n
la regla de redondeo, los cálculos pueden ir progresivamente hacia arriba, mientras que cuando se usa la ronda para igualar el teorema, esto no puede ocurrir. A lo largo del
resto de este documento, se utilizará una ronda para igualar.

Una aplicación de redondeo exacto ocurre en aritmética de precisión múltiple. Hay dos enfoques básicos para una mayor precisión. Un enfoque representa números de punto
flotante utilizando una gran mantisa, que se almacena en una matriz de palabras y códigos de las rutinas para la manipulación de estos números en lenguaje ensamblador. El
segundo enfoque representa números de punto flotante de mayor precisión como una matriz de números de punto flotante ordinarios, donde la adición de los elementos de la
matriz con precisión infinita recupera el número de punto flotante de alta precisión. Es este segundo enfoque que se discutirá aquí. La ventaja de usar una matriz de números
de punto flotante es que puede ser codificado portable en un lenguaje de alto nivel, pero requiere aritmética exactamente redondeado.

La clave para la multiplicación en este sistema está representando un producto de x y como una suma, donde cada sumando tiene la misma precisión que x y y . Esto se
puede hacer dividiendo x y y . Escribiendo x = x + x l y y = y + y l , el producto exacto es
h h

xy=x y +x y +x y +x y .
h h h l l h l l

Si x e y tienen significados de p bits, los sumandos también tendrán significados de p bits siempre que x , x ,y , y puedan representarse utilizando [ p / 2 ] bits. Cuando
l h h l
p es par, es fácil encontrar una división. El número x .x ... x se puede escribir como la suma de x .x ... x y 0.0 ... 0 x ... x . Cuando p es impar,
0 1 p-1 0 1 p/2-1 p/2 p-1
este simple método de división no funcionará. Sin embargo, se puede ganar un bit extra usando números negativos. Por ejemplo, si = 2, p = 5 y x = .10111, x se puede
dividir como x = .11 y x = -.00001. Hay más de una forma de dividir un número. Un método de división que es fácil de calcular se debe a Dekker [1971], pero requiere
h l
más que un solo dígito de guarda.

Teorema 6

Sea p la precisión de punto flotante, con la restricción de que p es par cuando > 2 , y supongamos que las operaciones de punto flotante están exactamente redondeadas.
Entonces, si k = [p / 2] es la mitad de la precisión (redondeado hacia arriba) y m = k + 1 , x se puede dividir como x = x h + x l , donde

x = (m x) (m x x), x l = x x h ,
h
y cada x es representable usando [p / 2] bits de precisión.
i

2
Para ver cómo funciona este teorema en un ejemplo, vamos a = 10, p = 4, b = 3.476, a = 3.463 yc = 3.479. Entonces b - ac redondeado al número de punto flotante más
2
cercano es .03480, mientras que b b = 12.08, a c = 12.05, y por lo tanto, el valor calculado de b - ac es .03. Este es un error de 480 ulps. Usando el teorema 6 para escribir
2 se 2 2 2
b = 3.5 - .024, a = 3.5 - .037, y c = 3.5 - .021, b convierte en 3.5 -2 × 3.5 × .024 + .024 . Cada sumando es exacto, entonces b = 12.25 - .168 + .000576,
2
donde la suma se deja sin evaluar en este punto. De manera similar, ac = 3.5 - (3.5 × .037 + 3.5 × .021) + .037 × .021 = 12.25 - .2030 +.000777. Finalmente, al restar
2
estas dos series término por término se obtiene una estimación para b - ac de 0 .0350 .000201 = .03480, que es idéntica al resultado exactamente redondeado. Para
demostrar que el Teorema 6 realmente requiere un redondeo exacto, considere p = 3, = 2 y x= 7. Entonces m = 5, mx = 35, y m x = 32. Si la resta se realiza con un solo
dígito de guarda, entonces ( m x ) x = 28. Por lo tanto, x h = 4 y x l = 3, por lo tanto x l no es representable con [ p / 2 ] = 1 bit.

Como ejemplo final de redondeo exacto, considere dividir m por 10. El resultado es un número de punto flotante que en general no será igual a m / 10. Cuando = 2, al
multiplicar m / 10 por 10 se restaurará m , siempre que se esté utilizando el redondeo exacto. En realidad, un hecho más general (debido a Kahan) es cierto. La prueba es
ingeniosa, pero los lectores que no estén interesados en estos detalles pueden pasar a la sección El estándar IEEE .

Teorema 7

Cuando = 2 , si m y n son enteros con | m | < 2 p - 1 y n tiene la forma especial n = 2 i + 2 j , luego (m n) n = m, siempre que las operaciones de punto flotante estén
exactamente redondeadas.

Prueba

-1
La escala por una potencia de dos es inofensiva, ya que cambia solo el exponente, no el significante. Si q = m / n , entonces escale n de modo que 2 p n <2 p y m escale de
-2
manera que 1/2 < q <1. Por lo tanto, 2 p < m <2 p . Como m tiene p bits significativos, tiene como máximo un bit a la derecha del punto binario. Cambiar el signo de m es
inofensivo, entonces asuma que q > 0.
Si = m n , para demostrar el teorema se requiere demostrar que

(9)

Esto se debe a que m tiene como máximo 1 bit a la derecha del punto binario, por lo que n redondeará a m . Para hacer frente a la mitad del caso cuando | n - m | = 1/4,
-1
tenga en cuenta que desde la inicial sin escala m tuvo | m | <2 p , su bit de orden inferior era 0, por lo que el bit de orden inferior de m escalado también es 0. Por lo tanto,
los casos a medio camino se redondearán a m .
Supongamos que q =. q q ... , y let =. q q ... q 1. Para estimar | n - m |, primer cálculo
1 2 1 2 p

+1
| -q|=|N/2p -m/n|

-1 -1
donde N es un entero impar. Como n = 2 i + 2 j y 2 p n <2 p , debe ser que n = 2 p + 2 k para algunos k p - 2, y por lo tanto

El numerador es un número entero, y puesto que N es impar, es, de hecho, un número entero impar. Así,
+1-
| -q| 1/(n2p k ).

10
Supongamos q < (el caso q > es similar). Entonces n < m , y

| mn | = mn = n ( q- )=n(q-( -2 -p-1 ))
-1
= (2 p +2 k ) 2 -p-1 -2 -p-1 + k =

11
Esto establece (9) y demuestra el teorema. z

El teorema es válido para cualquier base , siempre que 2 i + 2 j se reemplace por i + j . Sin embargo, a medida que aumenta, los denominadores de la forma i + j están cada
vez más separados.

Ahora estamos en condiciones de responder a la pregunta: ¿importa si las operaciones aritméticas básicas introducen un poco más de error de redondeo del necesario? La
respuesta es que sí importa, porque las operaciones básicas precisas nos permiten probar que las fórmulas son "correctas" en el sentido de que tienen un pequeño error
relativo. La sección Cancelación trató varios algoritmos que requieren dígitos de guarda para producir resultados correctos en este sentido. Sin embargo, si la entrada a esas
fórmulas son números que representan medidas imprecisas, los límites de los teoremas 3 y 4 se vuelven menos interesantes. La razón es que la cancelación benigna x - y
puede llegar a ser catastrófica si x y ySon solo aproximaciones a alguna cantidad medida. Pero las operaciones precisas son útiles incluso en el caso de datos inexactos,
porque nos permiten establecer relaciones exactas como las analizadas en los teoremas 6 y 7. Son útiles incluso si cada variable de punto flotante es solo una aproximación a
algún valor real.

El estándar IEEE
Hay dos estándares IEEE diferentes para el cálculo de punto flotante. IEEE 754 es un estándar binario que requiere = 2, p = 24 para precisión simple y p = 53 para precisión
doble [IEEE 1987]. También especifica el diseño preciso de bits en una precisión simple y doble. IEEE 854 permite = 2 o = 10 y, a diferencia de 754, no especifica cómo se
codifican los números de punto flotante en bits [Cody et al. 1984]. No requiere un valor particular para p , sino que especifica restricciones en los valores permitidos de p para
precisión simple y doble. El término Estándar IEEE se utilizará cuando se analicen las propiedades comunes a ambos estándares.

Esta sección ofrece un recorrido por el estándar IEEE. Cada subsección analiza un aspecto de la norma y por qué se incluyó. No es el propósito de este documento argumentar
que el estándar IEEE es el mejor estándar de punto flotante posible, sino más bien aceptar el estándar como se indica y proporcionar una introducción a su uso. Para más
detalles consulte las normas en sí mismas [IEEE 1987; Cody et al. 1984].

Formatos y Operaciones

Base

Está claro por qué IEEE 854 permite = 10. La base diez es cómo los humanos intercambian y piensan acerca de los números. Usar = 10 es especialmente apropiado para
las calculadoras, donde la calculadora muestra el resultado de cada operación en decimal.

Hay varias razones por las que IEEE 854 requiere que si la base no es 10, debe ser 2. La sección Relative Error y Ulps mencionó una razón: los resultados de los análisis de
errores son mucho más estrictos cuando es 2 debido a un error de redondeo de .5 ulp Las fluctuaciones por un factor de cuando se computan como un error relativo, y los
análisis de errores casi siempre son más simples cuando se basan en un error relativo. Una razón relacionada tiene que ver con la precisión efectiva para bases grandes.
Considere = 16, p = 1 comparado con = 2, p = 4. Ambos sistemas tienen 4 bits de significand. Considere el cálculo de 15/8. Cuando = 2, 15 se representa como 1.111 ×
3 0 0
2 , y 15/8 como 1.111 ×2 . Entonces 15/8 es exacto. Sin embargo, cuando = 16, 15 se representa como F × 16 , donde F es el dígito hexadecimal para 15. Pero 15/8
0
se representa como 1 × 16 , que solo tiene un bit correcto. En general, la base 16 puede perder hasta 3 bits, de modo que una precisión de p dígitos hexadecimales puede
tener una precisión efectiva tan baja como 4 p - 3 en lugar de 4 p bits binarios. Como los grandes valores tienen estos problemas, ¿por qué eligió IBM? = 16 para su sistema
/ 370? Solo IBM lo sabe con certeza, pero hay dos razones posibles. El primero es el rango exponencial aumentado. La precisión simple en el sistema / 370 tiene = 16, p =
6. Por lo tanto, el significado requiere 24 bits. Como esto debe encajar en 32 bits, deja 7 bits para el exponente y uno para el bit de signo. Por lo tanto, la magnitud de los
números representables varía de aproximadamente a aproximadamente = . Para obtener un rango de exponente similar cuando = 2 requeriría 9 bits de
exponente, dejando solo 22 bits para el significando. Sin embargo, solo se señaló que cuando = 16, la precisión efectiva puede ser tan baja como 4 p - 3 = 21 bits. Peor aún,
cuando = 2 es posible obtener un poco más de precisión (como se explica más adelante en esta sección), por lo que la máquina = 2 tiene 23 bits de precisión para
comparar con un rango de 21 a 24 bits para la máquina = 16.

Otra posible explicación para elegir = 16 tiene que ver con el cambio. Al agregar dos números de punto flotante, si sus exponentes son diferentes, uno de los significandos
tendrá que desplazarse para hacer que los puntos de la raíz se alineen, lo que ralentizará la operación. En el sistema = 16, p = 1, todos los números entre 1 y 15 tienen el
mismo exponente, por lo que no se requiere cambio al agregar cualquiera de los ( ) = 105 pares posibles de números distintos de este conjunto. Sin embargo, en el
sistema = 2, p = 4, estos números tienen exponentes que van de 0 a 3, y se requieren cambios para 70 de los 105 pares.
En la mayoría de los equipos modernos, el rendimiento obtenido al evitar un cambio para un subconjunto de operandos es insignificante, por lo que la pequeña oscilación de
12
= 2 la convierte en la base preferible. Otra ventaja de usar = 2 es que hay una manera de ganar un poco de significado adicional. Dado que los números de punto flotante
siempre están normalizados, el bit más significativo del significado es siempre 1, y no hay razón para perder un poco de almacenamiento representándolo. Se dice que los
formatos que usan este truco tienen un bit oculto . Ya se señaló en Formatos de punto flotante que esto requiere una convención especial para 0. El método dado fue que un
exponente de e - 1 y un significado de todos los ceros no representan , sino más bien 0.
min

La precisión simple IEEE 754 se codifica en 32 bits utilizando 1 bit para el signo, 8 bits para el exponente y 23 bits para el significado. Sin embargo, utiliza un bit oculto, por lo
que el significado es de 24 bits ( p = 24), aunque está codificado utilizando solo 23 bits.

Precisión

El estándar IEEE define cuatro precisiones diferentes: simple, doble, simple-extendida y doble-extendida. En IEEE 754, la precisión simple y doble corresponde
aproximadamente a lo que proporciona la mayoría de los equipos de punto flotante. La precisión simple ocupa una sola palabra de 32 bits, la doble precisión dos palabras de
32 bits consecutivas. La precisión extendida es un formato que ofrece al menos un poco más de precisión y rango de exponentes ( TABLA D-1 ).
TABLA D-1 Parámetros de formato IEEE 754
Formato
Parámetro
Soltero Single-Extended Doble Doble extensión
pag 24 32 53 64
e +127 1023 +1023 > 16383
max

e -126 -1022 -1022 -16382


min

Ancho de exponente en bits 8 11 11 15

Formato de ancho en bits 32 43 64 79

El estándar IEEE solo especifica un límite inferior sobre cuántos bits adicionales proporciona la precisión extendida. El formato doble extendido mínimo permitido a veces se
denomina formato de 80 bits , aunque la tabla lo muestre utilizando 79 bits. La razón es que las implementaciones de hardware de precisión extendida normalmente no usan
13
un bit oculto, por lo que usarían 80 en lugar de 79 bits.

El estándar pone el mayor énfasis en la precisión extendida, sin hacer recomendaciones con respecto a la doble precisión, pero recomienda encarecidamente que las
implementaciones deben admitir el formato extendido correspondiente al formato básico más amplio admitido, ...

Una motivación para la precisión extendida proviene de las calculadoras, que a menudo muestran 10 dígitos, pero usan 13 dígitos internamente. Al mostrar solo 10 de los 13
dígitos, la calculadora le aparece al usuario como una "caja negra" que calcula los exponenciales, cosenos, etc. con 10 dígitos de precisión. Para que la calculadora pueda
calcular funciones como exp, log y cos hasta 10 dígitos con una eficiencia razonable, necesita algunos dígitos adicionales para trabajar. No es difícil encontrar una expresión
racional simple que se aproxime al registro con un error de 500 unidades en el último lugar. Por lo tanto, la computación con 13 dígitos da una respuesta correcta a 10 dígitos.
Al mantener estos 3 dígitos adicionales ocultos, la calculadora presenta un modelo simple para el operador.

La precisión extendida en el estándar IEEE cumple una función similar. Permite que las bibliotecas calculen de manera eficiente cantidades dentro de aproximadamente .5 ulp
en precisión simple (o doble), lo que brinda al usuario de esas bibliotecas un modelo simple, a saber, que cada operación primitiva, ya sea una simple multiplicación o una
invocación de registro, devuelve un valor exacto dentro de aproximadamente .5 ulp. Sin embargo, cuando se utiliza la precisión extendida, es importante asegurarse de que
su uso sea transparente para el usuario. Por ejemplo, en una calculadora, si la representación interna de un valor visualizado no se redondea a la misma precisión que la
pantalla, el resultado de otras operaciones dependerá de los dígitos ocultos y aparecerá impredecible para el usuario.

Para ilustrar aún más la precisión extendida, considere el problema de la conversión entre precisión simple y decimal IEEE 754. Idealmente, los números de precisión simple
se imprimirán con suficientes dígitos para que cuando se lea el número decimal, se pueda recuperar el número de precisión simple. Resulta que 9 dígitos decimales son
suficientes para recuperar un solo número binario de precisión (consulte la sección Conversión de binario a decimal)). Al convertir un número decimal de nuevo a su
representación binaria única, un error de redondeo tan pequeño como 1 ulp es fatal, ya que dará la respuesta incorrecta. Aquí es una situación donde la precisión extendida es
vital para un algoritmo eficiente. Cuando está disponible una extensión única, existe un método muy sencillo para convertir un número decimal a uno binario de precisión
9 32 9
simple. Primero lea los 9 dígitos decimales como un entero N , ignorando el punto decimal. Desde la TABLA D-1 , p 32, y desde 10 <2 4.3 × 10 , N puede
P
representarse exactamente en extensión simple. A continuación encuentra la potencia adecuada 10 necesaria para escalar N . Esta será una combinación del exponente del
| |
número decimal, junto con la posición del punto decimal ignorado (hasta ahora). Calcular 10 P . Si | P | 13, entonces esto también se representa exactamente, porque
13 13 13 13 32 | |
10 =2 5 ,y5 <2 . Finalmente multiplica (o divide si p <0) N y 10 P . Si esta última operación se realiza exactamente, entonces se recupera el número
binario más cercano. La sección de conversión binario a decimalmuestra cómo hacer la última multiplicación (o división) exactamente. Así para | P | 13, el uso del formato
extendido único permite que los números decimales de 9 dígitos se conviertan al número binario más cercano (es decir, redondeados exactamente). Si | P | > 13, entonces el
simple-extendido no es suficiente para que el algoritmo anterior calcule siempre el equivalente binario redondeado exactamente, pero Coonen [1984] muestra que es
suficiente para garantizar que la conversión de binario a decimal y posterior recuperará el número binario original .

Si se admite la precisión doble, entonces el algoritmo anterior se ejecutaría con precisión doble en lugar de extensión simple, pero para convertir la precisión doble en un
número decimal de 17 dígitos, la versión de atrás requeriría el formato extendido doble.

Exponente

Dado que el exponente puede ser positivo o negativo, debe elegirse algún método para representar su signo. Dos métodos comunes para representar números firmados son
signo / magnitud y el complemento de dos. Signo / magnitud es el sistema utilizado para el signo del significante en los formatos IEEE: un bit se usa para mantener el signo,
el resto de los bits representan la magnitud del número. La representación complementaria de los dos se usa a menudo en aritmética de enteros. En este esquema, un
p-1 p-1 p
número en el rango [-2 ,2 - 1] está representado por el número no negativo más pequeño que es congruente con el módulo 2 .

El estándar binario IEEE no utiliza ninguno de estos métodos para representar el exponente, sino que utiliza una representación sesgada . En el caso de precisión simple,
donde el exponente se almacena en 8 bits, el sesgo es 127 (para precisión doble es 1023). Lo que esto significa es que si el valor de los bits del exponente se interpreta
como un entero sin signo, entonces el exponente del número de punto flotante es - 127. Esto a menudo se denomina exponente no sesgado para distinguirlo del exponente
sesgado .

Con referencia a la TABLA D-1 , la precisión simple tiene e = 127 y e = -126. La razón de tener | e |<e es para que el recíproco del número más pequeño
max min min max
no se desborde. Si bien es cierto que el recíproco del mayor número se desbordará, el subdesbordamiento suele ser menos grave que el desbordamiento. La sección
Base explicó que e - 1 se usa para representar 0, y Special Quantities introducirá un uso para e + 1. En la precisión simple IEEE, esto significa que los exponentes
min max
sesgados varían entree - 1 = -127 y e + 1 = 128, mientras que los exponentes no sesgados oscilan entre 0 y 255, que son exactamente los números no negativos
min max
que se pueden representar utilizando 8 bits.

Operaciones

El estándar IEEE requiere que el resultado de la suma, resta, multiplicación y división sea exactamente redondeado. Es decir, el resultado debe calcularse exactamente y luego
redondearse al número de punto flotante más cercano (usando redondeo para igualar). La sección Guard Digits señaló que calcular la diferencia exacta o la suma de dos
números de punto flotante puede ser muy costoso cuando sus exponentes son sustancialmente diferentes. Esa sección introdujo dígitos de guarda, que proporcionan una
forma práctica de calcular las diferencias al tiempo que garantizan que el error relativo sea pequeño. Sin embargo, calcular con un solo dígito de guarda no siempre dará la
misma respuesta que calcular el resultado exacto y luego redondear. Al introducir un segundo dígito de guardia y un tercer stickypoco, las diferencias se pueden calcular a un
costo un poco más alto que con un solo dígito de guarda, pero el resultado es el mismo que si la diferencia se computara exactamente y luego se redondeara [Goldberg
1990]. Por lo tanto, la norma se puede implementar de manera eficiente.

Una razón para especificar completamente los resultados de las operaciones aritméticas es mejorar la portabilidad del software. Cuando un programa se mueve entre dos
máquinas y ambas admiten aritmética IEEE, entonces, si cualquier resultado intermedio difiere, debe ser por errores de software, no por diferencias en aritmética. Otra
ventaja de la especificación precisa es que facilita el razonamiento sobre el punto flotante. Las pruebas sobre el punto flotante son bastante difíciles, sin tener que lidiar con
múltiples casos derivados de múltiples tipos de aritmética. Del mismo modo que se puede probar que los programas enteros son correctos, también lo pueden hacer los
programas de punto flotante, aunque lo que se prueba en ese caso es que el error de redondeo del resultado satisface ciertos límites. El teorema 4 es un ejemplo de tal
prueba. Estas pruebas se hacen mucho más fáciles cuando las operaciones sobre las que se razonan se especifican con precisión. Una vez que se demuestre que un algoritmo
es correcto para la aritmética IEEE, funcionará correctamente en cualquier máquina que admita el estándar IEEE.

Brown [1981] ha propuesto axiomas para punto flotante que incluyen la mayoría del hardware de punto flotante existente. Sin embargo, las pruebas en este sistema no
pueden verificar los algoritmos de las secciones Cancelación y Operaciones Exactamente Redondeadas , que requieren características que no están presentes en todo el
hardware. Además, los axiomas de Brown son más complejos que la simple definición de las operaciones que se realizarán exactamente y luego se redondearán. Por lo tanto,
probar los teoremas de los axiomas de Brown es generalmente más difícil que probarlos suponiendo que las operaciones son exactamente redondeadas.
No hay un acuerdo completo sobre qué operaciones debe cubrir una norma de punto flotante. Además de las operaciones básicas +, -, × y /, el estándar IEEE también
especifica que la raíz cuadrada, el resto y la conversión entre entero y punto flotante se redondeen correctamente. También requiere que la conversión entre formatos internos
y decimales se redondee correctamente (excepto para números muy grandes). Kulisch y Miranker [1986] han propuesto agregar un producto interno a la lista de operaciones
que se especifican con precisión. Señalan que cuando los productos internos se calculan en aritmética IEEE, la respuesta final puede ser bastante errónea. Por ejemplo, las
-30 30 30 -30 -30
sumas son un caso especial de productos internos, y la suma ((2 × 10 + 10 ) - 10 ) - 10 es exactamente igual a 10 , pero en una máquina con aritmética
-30
IEEE, el resultado calculado será -10 . Es posible calcular productos internos dentro de 1 ulp con menos hardware del que se necesita para implementar un multiplicador
14 15
rápido [Kirchner y Kulish 1987].

Todas las operaciones mencionadas en el estándar deben redondearse exactamente, excepto la conversión entre decimal y binario. La razón es que se conocen algoritmos
eficientes para redondear exactamente todas las operaciones, excepto la conversión. Para la conversión, los algoritmos eficientes más conocidos producen resultados que son
ligeramente peores que los redondeados exactamente [Coonen 1984].

El estándar IEEE no requiere que las funciones trascendentales estén redondeadas exactamente debido al dilema del creador de la mesa . Para ilustrar esto, supongamos que
usted está haciendo una tabla de la función exponencial a 4 lugares. Entonces exp (1.626) = 5.0835. ¿Debería esto redondearse a 5.083 o 5.084? Si exp (1.626) se calcula
con más cuidado, se hace 5,08350. Y luego 5.083500. Y luego 5.0835000. Dado que exp es trascendental, esto podría continuar arbitrariamente mucho antes de distinguir si
exp (1.626) es 5.083500 ... 0 ddd o 5.0834999 ... 9 ddd. Por lo tanto, no es práctico especificar que la precisión de las funciones trascendentales sea la misma que si se
computaran con una precisión infinita y luego se redondearan. Otro enfoque sería especificar funciones trascendentales algorítmicamente. Pero no parece haber un solo
16
algoritmo que funcione bien en todas las arquitecturas de hardware. La aproximación racional, CORDIC, y las tablas grandes son tres técnicas diferentes que se utilizan
para calcular los trascendentales en máquinas contemporáneas. Cada uno es apropiado para una clase diferente de hardware, y en la actualidad, ningún algoritmo único
funciona de manera aceptable en la amplia gama de hardware actual.

Cantidades especiales
TM
En algún hardware de punto flotante, cada patrón de bits representa un número válido de punto flotante. El IBM System / 370 es un ejemplo de esto. Por otro lado, el VAX
reserva algunos patrones de bits para representar números especiales llamados operandos reservados . Esta idea se remonta al CDC 6600, que tenía patrones de bits para las
cantidades especiales INDEFINITEy INFINITY.

El estándar IEEE continúa en esta tradición y tiene NaN ( no un número ) e infinitos. Sin ninguna cantidad especial, no hay una buena manera de manejar situaciones
excepcionales como tomar la raíz cuadrada de un número negativo, aparte del cálculo abortivo. Bajo IBM System / 370 FORTRAN, la acción predeterminada en respuesta a la
computación de la raíz cuadrada de un número negativo como -4 resulta en la impresión de un mensaje de error. Dado que cada patrón de bits representa un número válido,
el valor de retorno de la raíz cuadrada debe ser un número de punto flotante. En el caso de System / 370 FORTRAN, se devuelve. En la aritmética IEEE, se devuelve un
NaN en esta situación.

El estándar IEEE especifica los siguientes valores especiales (vea la TABLA D-2 ): ± 0, números desnormalizados, ± y NaNs (hay más de un NaN, como se explica en la
siguiente sección). Todos estos valores especiales están codificados con exponentes de e +1oe - 1 (ya se señaló que 0 tiene un exponente de e - 1).
max min min

TABLA D-2 Valores especiales IEEE 754


Exponente Fracción Representa
e=e f=0 ±0
min - 1

e=e f
min - 1 0

e e e max e
min - 1. f × 2
e=e f=0
max + 1 ±

e=e f
max + 1 0 Yaya

NaNs

Tradicionalmente, el cálculo de 0/0 o se ha tratado como un error irrecuperable que hace que un cálculo se detenga. Sin embargo, hay ejemplos en los que tiene sentido
que una computación continúe en tal situación. Considere una subrutina que encuentre los ceros de una función f , por ejemplo zero(f). Tradicionalmente, los buscadores de
cero requieren que el usuario ingrese un intervalo [ a , b ] en el que se define la función y sobre el cual buscará el buscador de cero. Es decir, la subrutina se llama como
zero(f, a,b). Un buscador de cero más útil no requeriría que el usuario ingrese esta información adicional. Este buscador de cero más general es especialmente apropiado para
las calculadoras, donde es natural simplemente teclear una función, y es incómodo tener que especificar el dominio. Sin embargo, es fácil ver por qué la mayoría de los
buscadores de cero requieren un dominio. El buscador de cero hace su trabajo al sondear la función fen varios valores. Si buscaba un valor fuera del dominio de f, el código
para fbien podría calcular 0/0 o , y el cálculo se detendría, abortando innecesariamente el proceso de búsqueda de cero.

Este problema se puede evitar introduciendo un valor especial llamado NaN, y especificando que el cálculo de expresiones como 0/0 y produzca NaN, en lugar de
detenerse. En la TABLA D-3 se proporciona una lista de algunas de las situaciones que pueden causar un NaN . Luego, cuando las zero(f)sondas están fuera del dominio f, el
código para fdevolver NaN, y el buscador de cero puede continuar. Es decir, zero(f)no está "castigado" por hacer una suposición incorrecta. Con este ejemplo en mente, es fácil
ver cuál debería ser el resultado de combinar un NaN con un número de punto flotante ordinario. Supongamos que la declaración final de fes return(-b + sqrt(d))/(2*a). Si d <0,
entonces fdebe devolver un NaN. Dado que d <0, sqrt(d)es un NaN, y-b + sqrt(d)será un NaN, si la suma de un NaN y cualquier otro número es un NaN. De manera similar, si
un operando de una operación de división es un NaN, el cociente debe ser un NaN. En general, cuando un NaN participa en una operación de punto flotante, el resultado es
otro NaN.
TABLA D-3 Operaciones que
producen un NaN
Operación NaN Producido por
+ + (- )

× 0×

/ 0/0, /
REM x REM 0, y REM

(cuando x < 0)

Otro método para escribir un solucionador de cero que no requiere que el usuario ingrese un dominio es usar señales. El buscador de cero podría instalar un controlador de
señales para las excepciones de punto flotante. Luego, si fse evaluó fuera de su dominio y se generó una excepción, el control se devolvería al solucionador cero. El problema
con este enfoque es que cada idioma tiene un método diferente para manejar las señales (si es que tiene un método) y, por lo tanto, no tiene ninguna esperanza de
portabilidad.

En IEEE 754, los NaN a menudo se representan como números de punto flotante con el exponente e + 1 y significandos distintos de cero. Las implementaciones son libres
max
de colocar información dependiente del sistema en el significado. Por lo tanto, no existe una NaN única, sino una familia completa de NaN. Cuando se combinan un NaN y un
número de punto flotante ordinario, el resultado debe ser el mismo que el operando NaN. Por lo tanto, si el resultado de un cálculo largo es un NaN, la información
dependiente del sistema en el significado será la información que se generó cuando se generó el primer NaN en el cálculo. En realidad, hay una advertencia a la última
declaración. Si ambos operandos son NaN, el resultado será uno de esos NaN, pero puede que no sea el NaN el que se generó primero.

infinito

Al igual que los NaN proporcionan una forma de continuar un cálculo cuando se encuentran expresiones como 0/0 o se encuentran, los infinitos proporcionan una manera
de continuar cuando se produce un desbordamiento. Esto es mucho más seguro que simplemente devolver el mayor número representable. Como ejemplo, considere
70 70 2 98
computar , cuando = 10, p = 3, y e = 98. Si x = 3 × 10 e y = 4 × 10 , entonces x se desbordará y se reemplazará por 9.99 × 10 . Del mismo modo
max
2 2. 2 98
y ,yx +y se desbordarán cada uno, y serán reemplazados por 9.99 × 10 . Así que el resultado final será , lo que es drásticamente incorrecto: la
70 2 2 2 2
respuesta correcta es 5 × 10 . En la aritmética IEEE, el resultado de x es , como es y ,x +y y . Entonces, el resultado final es , que es más seguro que
17
devolver un número de punto flotante ordinario que no se encuentra cerca de la respuesta correcta.
La división de 0 por 0 da como resultado un NaN. Un número distinto de cero dividido por 0, sin embargo, devuelve infinito: 1/0 = , -1/0 = - . La razón para la distinción
es la siguiente: si f ( x ) 0 y g ( x ) 0 cuando x se aproxima a algún límite, entonces f ( x ) / g ( x ) podría tener cualquier valor. Por ejemplo, cuando f ( x ) = sen x y g ( x )
= x , entonces f ( x ) / g ( x) 1 como x 0. Pero cuando f ( x ) = 1 - cos x , f ( x ) / g ( x ) 0. Al pensar en 0/0 como la situación límite de un cociente de dos números
muy pequeños, 0 / 0 podría representar cualquier cosa. Así, en el estándar IEEE, 0/0 da como resultado un NaN. Pero cuando c > 0, f ( x ) c , y g ( x ) 0, entonces f ( x ) / g ( x
) ± , para cualquier función analítica f y g. Si g ( x ) < 0 para x pequeña, luego f ( x ) / g ( x ) - de lo contrario, el límite es + . Por lo tanto, el estándar IEEE
define c / 0 = ± , siempre y cuando c 0. El signo de depende de los signos de c y 0 de la forma habitual, de modo que -10/0 = - , y -10 / -0 = + . Puede distinguir
entre obtener debido al desbordamiento y obtener debido a la división por cero al verificar los indicadores de estado (que se analizarán en detalle en la sección
Indicadores).). El indicador de desbordamiento se establecerá en el primer caso, la división por cero en el segundo.

La regla para determinar el resultado de una operación que tiene infinito como operando es simple: reemplace el infinito con un número finito x y tome el límite como x .
Así 3 / = 0, porque

Del mismo modo, 4 - = - , y = . Cuando el límite no existe, el resultado es un NaN, por lo tanto / será un NaN (la TABLA D-3 tiene ejemplos adicionales). Esto
concuerda con el razonamiento utilizado para concluir que 0/0 debe ser un NaN.

Cuando una subexpresión se evalúa como un NaN, el valor de la expresión completa también es un NaN. Sin embargo, en el caso de ± , el valor de la expresión podría ser
un número de punto flotante ordinario debido a reglas como 1 / = 0. Este es un ejemplo práctico que hace uso de las reglas para la aritmética infinita. Considere computar
2
la función x / ( x + 1). Esta es una mala fórmula, porque no solo se desbordará cuando x sea más grande que , sino que la aritmética infinita dará una respuesta
2 -1
incorrecta porque dará 0, en lugar de un número cercano a 1 / x . Sin embargo, x / ( x + 1) se puede reescribir como 1 / ( x +x ). Esta expresión mejorada no se
-1
desbordará prematuramente y, debido a la aritmética del infinito, tendrá el valor correcto cuando x = 0: 1 / (0 + 0 ) = 1 / (0 + ) = 1 / = 0. Sin aritmética del infinito, la
-1
expresión 1 / ( x + x ) requiere una prueba para x = 0, que no solo agrega instrucciones adicionales, sino que también puede interrumpir una tubería. Este ejemplo ilustra
un hecho general, a saber, que la aritmética infinita a menudo evita la necesidad de verificar casos especiales; sin embargo, las fórmulas deben inspeccionarse
2
cuidadosamente para asegurarse de que no tengan un comportamiento espurio en el infinito (como sí lo hizo x / ( x + 1)).

Firmado cero

Cero está representado por el exponente e - 1 y un significado cero. Como el bit de signo puede tomar dos valores diferentes, hay dos ceros, +0 y -0. Si se hiciera una
min
distinción al comparar +0 y -0, las pruebas simples if (x = 0)tendrían un comportamiento impredecible, dependiendo del signo de x. Por lo tanto, el estándar IEEE define la
comparación de manera que +0 = -0, en lugar de -0 <+0. Aunque siempre sería posible ignorar el signo de cero, el estándar IEEE no lo hace. Cuando una multiplicación o
división implica un cero firmado, las reglas de signo habituales se aplican al calcular el signo de la respuesta. Entonces 3 · (+0) = +0, y + 0 / -3 = -0. Si cero no tenía un
signo, entonces la relación 1 / (1 / x ) = xno se mantendría cuando x = ± . La razón es que 1 / - y 1 / + dan como resultado 0, y 1/0 da como resultado + , la
información de signo se ha perdido. Una forma de restaurar la identidad 1 / (1 / x ) = x es tener solo un tipo de infinito, sin embargo, esto resultaría en la desastrosa
consecuencia de perder el signo de una cantidad desbordada.

Otro ejemplo del uso de cero firmado se refiere al subdesbordamiento y las funciones que tienen una discontinuidad en 0, como el registro. En la aritmética IEEE, es natural
definir log 0 = - y log x para ser un NaN cuando x <0. Supongamos que x representa un número negativo pequeño que ha subido a cero. Gracias al cero firmado, x será
negativo, por lo que el registro puede devolver un NaN. Sin embargo, si no hubiera un cero firmado, la función de registro no podría distinguir un número negativo
infrautilizado de 0 y, por lo tanto, tendría que devolver - . Otro ejemplo de una función con una discontinuidad en cero es la función signum, que devuelve el signo de un
número.

Probablemente el uso más interesante del cero con signo ocurre en la aritmética compleja. Para tomar un ejemplo simple, considere la ecuación . Esto es
ciertamente cierto cuando z 0. Si z = -1, el cálculo obvio da y . Así ,! El problema se puede remontar al hecho de que la raíz
cuadrada es multivaluada y no hay forma de seleccionar los valores para que sea continua en todo el plano complejo. Sin embargo, la raíz cuadrada es continua si se excluye
de consideración un corte de rama consistente en todos los números reales negativos. Esto deja el problema de qué hacer para los números reales negativos, que son de la
forma - x + i 0, donde x> 0. El cero firmado proporciona una manera perfecta de resolver este problema. Los números de la forma x + i (+0) tienen un signo y los
números de la forma x + i (-0) en el otro lado del corte de rama tienen el otro signo . De hecho, las fórmulas naturales para la computación darán estos resultados.

Volver a . Si z = 1 = -1 + i 0, entonces
2 2
1 / z = 1 / (- 1 + i 0) = [(-1 - i 0)] / [(- 1 + i 0) (- 1 - i 0)] = (-1 - i 0) / ((-1) -0 ) = -1 + i (-0),

y así , mientras . Así, la aritmética IEEE conserva esta identidad para todos los z . Kahan [1987] da algunos ejemplos más sofisticados.
Aunque distinguir entre +0 y -0 tiene ventajas, en ocasiones puede resultar confuso. Por ejemplo, el cero firmado destruye la relación x = y 1 / x = 1 / y , que es falso
cuando x = +0 e y = -0. Sin embargo, el comité del IEEE decidió que las ventajas de utilizar el signo de cero superaban las desventajas.

Números desnormalizados

-97 -97
Considere los números de punto flotante normalizados con = 10, p = 3 y e = -98. Los números x = 6.87 × 10 y y = 6.81 × 10 parecen ser números de punto
min
-98
flotante perfectamente normales, que son más que un factor de 10 más grande que el número de punto flotante más pequeño 1.00 × 10 . Sin embargo, tienen una
-97 -99
propiedad extraña: x y = 0, aunque x y ! La razón es que x - y = .06 × 10 = 6.0 × 10 es demasiado pequeño para ser representado como un número
normalizado, por lo que debe vaciarse a cero. ¿Qué tan importante es preservar la propiedad?
(10) x = y x - y = 0?

Es muy fácil imaginar cómo se escribe el fragmento de código , y mucho más tarde, tener un programa falla debido a una división espuria por cero. La búsqueda de errores
como este es frustrante y consume mucho tiempo. En un nivel más filosófico, los libros de texto de ciencias de la computación a menudo señalan que aunque actualmente no
es práctico probar que los programas grandes son correctos, diseñar programas con la idea de probarlos a menudo resulta en un mejor código. Por ejemplo, la introducción de
invariantes es bastante útil, incluso si no se van a utilizar como parte de una prueba. El código de punto flotante es como cualquier otro código: ayuda a tener datos
demostrables de los cuales depender. Por ejemplo, al analizar la fórmula (6) , fue muy útil saber que x / 2 < y <2 x xif (x y) then z = 1/(x-y) y = x - y . De manera
similar, saber que (10) es verdadero facilita la escritura de código de punto flotante confiable. Si solo es cierto para la mayoría de los números, no se puede usar para probar
nada.
18
El estándar IEEE utiliza números denormalizados , que garantizan (10) , así como otras relaciones útiles. Son la parte más controvertida de la norma y probablemente
explicaron la larga demora en obtener la aprobación de 754. La mayoría del hardware de alto rendimiento que dice ser compatible con IEEE no admite números
19
desnormalizados directamente, sino que atrapa cuando consume o produce denormales, y lo deja al software para simular el estándar IEEE. La idea detrás de los números
desnormalizados se remonta a Goldberg [1967] y es muy simple. Cuando el exponente es e , el significando no tiene que normalizarse, de modo que cuando = 10, p = 3
min
-98 -98
ye = -98, 1.00 × 10 ya no es el número más pequeño de punto flotante, porque 0.98 × 10 también es un número de punto flotante.
min

Hay un pequeño inconveniente cuando = 2 y se está utilizando un bit oculto, ya que un número con un exponente de e siempre tendrá un significado mayor o igual a 1.0
min
debido al bit inicial implícito. La solución es similar a la utilizada para representar 0, y se resume en la TABLA D-2 . El exponente e se usa para representar denormales.
min
Más formalmente, si los bits en el campo significand son b ,b , ... , b , y el valor del exponente es e , entonces cuando e > e - 1, el número que se representa es 1.
1 2 p -1 min
+1
b b ... b × 2 e mientras que cuando e = e - 1, el número que se representa es 0. b b ... b ×2e . El +1 en el exponente es necesario porque los
1 2 p-1 min 1 2 p-1
denormales tienen un exponente de e , no e - 1.
min min

-97 -97
Recuerde el ejemplo de = 10, p = 3, e = -98, x = 6.87 × 10 y y = 6.81 × 10 presentado al comienzo de esta sección. Con denormales, x - y no se vacía a cero
min
-98
sino que está representado por el número desnormalizado .6 × 10 . Este comportamiento se llama desbordamiento gradual . Es fácil verificar que (10) siempre se cumple
cuando se usa un flujo bajo gradual.
FIGURA D-2 Flush to Zero en comparación con el flujo gradual gradual

La FIGURA D-2 ilustra números desnormalizados. La línea de número superior en la figura muestra números de punto flotante normalizados. Observe la brecha entre 0 y el
número normalizado más pequeño . Si el resultado de un cálculo de punto flotante cae en este abismo, se descarga a cero. La línea de números inferior muestra lo
que sucede cuando se agregan denormales al conjunto de números de punto flotante. El "golfo" se rellena y, cuando el resultado de un cálculo es menor que , está
representado por el denormal más cercano. Cuando los números desnormalizados se agregan a la línea numérica, el espaciado entre los números de punto flotante
adyacentes varía de manera regular: los espacios adyacentes tienen la misma longitud o difieren en un factor de . Sin denormales, el
espaciado cambia bruscamente de a , que es un factor de , en lugar del cambio ordenado por un factor de . Debido a esto, muchos algoritmos que pueden
tener un error relativo grande para los números normalizados cercanos al umbral de subdesbordamiento se comportan bien en este rango cuando se usa el
subdesbordamiento gradual.

Sin un desbordamiento gradual, la expresión simple x - y puede tener un error relativo muy grande para las entradas normalizadas, como se vio anteriormente para x = 6.87
-97 -97
× 10 y y = 6.81 × 10 . Pueden ocurrir grandes errores relativos incluso sin cancelación, como muestra el siguiente ejemplo [Demmel 1984]. Considere la posibilidad
de dividir dos números complejos, a + ib y c + id . La formula obvia

·I

tiene el problema de que si alguno de los componentes del denominador c + id es más grande que , la fórmula se desbordará, aunque el resultado final pueda estar
dentro del rango. Un mejor método para calcular los cocientes es usar la fórmula de Smith:

(11)

-98 -98 -98 -98


La aplicación de la fórmula de Smith a (2 · 10 + i 10 ) / (4 · 10 + i (2 · 10 ) da la respuesta correcta de 0.5 con flujo progresivo gradual. Produce 0.4 con
descarga a cero, un error de 100 ulps. Es típico que los números desnormalizados garanticen los límites de error para los argumentos hasta 1.0 x .

Excepciones, banderas y trampas trampa.

Cuando se produce una condición excepcional como división por cero o desbordamiento en la aritmética IEEE, el valor predeterminado es entregar un resultado y continuar.
Típico de los resultados por defecto son NaN para 0/0 y , y para 1/0 y desbordamiento. Las secciones anteriores dieron ejemplos en los que proceder de una excepción
con estos valores predeterminados era lo razonable. Cuando se produce una excepción, también se establece un indicador de estado. Las implementaciones del estándar IEEE
deben proporcionar a los usuarios una forma de leer y escribir las marcas de estado. Las banderas son "pegajosos" en el que, una vez establecido, que permanecen ajustados
hasta que se borra de forma explícita. Probar las banderas es la única forma de distinguir 1/0, que es un infinito genuino de un desbordamiento.
2
A veces, la ejecución continua frente a condiciones de excepción no es apropiada. La sección Infinito dio el ejemplo de x / ( x + 1). Cuando x > , el denominador es
infinito, lo que da como resultado una respuesta final de 0, que es totalmente errónea. Aunque para esta fórmula el problema se puede resolver reescribiéndolo como 1 / ( x +
-1
x ), la reescritura no siempre puede resolver el problema. El estándar IEEE recomienda encarecidamente que las implementaciones permitan la instalación de controladores
de trampas. Luego, cuando se produce una excepción, se llama al controlador de trampas en lugar de establecer el indicador. El valor devuelto por el controlador de trampas
se utilizará como resultado de la operación. Es responsabilidad del manejador de trampas borrar o establecer el indicador de estado; de lo contrario, se permite que el valor
de la bandera no esté definido.

El estándar IEEE divide las excepciones en 5 clases: desbordamiento, subdesbordamiento, división por cero, operación inválida e inexacta. Hay una bandera de estado
separada para cada clase de excepción. El significado de las tres primeras excepciones es evidente. La operación no válida cubre las situaciones enumeradas en la TABLA D-3
y cualquier comparación que involucre un NaN. El resultado predeterminado de una operación que causa una excepción no válida es devolver un NaN, pero lo contrario no es
cierto. Cuando uno de los operandos de una operación es un NaN, el resultado es un NaN pero no se genera una excepción no válida a menos que la operación también
20
cumpla una de las condiciones de la TABLA D-3 .
TABLA D-4 Excepciones en IEEE 754 *

Excepción Resultado cuando las trampas están deshabilitadas Argumento para atrapar manejador

± -
rebosar o ± x max
redondo ( x 2 )

desbordamiento 0, o denormal redondo ( x 2 )

dividir entre cero ± operandos

inválido Yaya operandos

inexacto redondo ( x ) redondo ( x )

* x es el resultado exacto de la operación, = 192 para precisión simple, 1536 para doble y x = 1.11 ... 11 × .
max

La excepción inexacta se genera cuando el resultado de una operación de punto flotante no es exacto. En el sistema = 10, p = 3, 3.5 4.2 = 14.7 es exacto, pero 3.5 4.3
= 15.0 no es exacto (desde 3.5 · 4.3 = 15.05), y genera una excepción inexacta. La conversión de binario a decimal describe un algoritmo que usa la excepción inexacta. En
la TABLA D-4 se presenta un resumen del comportamiento de las cinco excepciones .

Hay un problema de implementación relacionado con el hecho de que la excepción inexacta se genera con tanta frecuencia. Si el hardware de punto flotante no tiene
indicadores propios, sino que interrumpe el sistema operativo para indicar una excepción de punto flotante, el costo de las excepciones inexactas podría ser prohibitivo. Este
costo puede evitarse manteniendo los indicadores de estado en el software. La primera vez que se produce una excepción, establezca el indicador de software para la clase
apropiada e indique al hardware de punto flotante que enmascare esa clase de excepciones. Luego, todas las excepciones adicionales se ejecutarán sin interrumpir el sistema
operativo. Cuando un usuario restablece ese indicador de estado, la máscara de hardware se vuelve a habilitar.

Manejadores de trampas

Un uso obvio para los manipuladores de trampas es la compatibilidad con versiones anteriores. Los códigos antiguos que esperan ser abortados cuando se producen
excepciones pueden instalar un controlador de trampas que aborta el proceso. Esto es especialmente útil para códigos con un bucle como do S until (x >= 100). Dado que
comparar un NaN con un número con <,, >, o = (pero no ) siempre devuelve falso, este código entrará en un bucle infinito si alguna vez se convierte en un NaN. x

Hay un uso más interesante para los manipuladores de trampas que aparece cuando se computan productos como los que potencialmente podrían desbordarse. Una solución
es utilizar logaritmos y, en su lugar , calcular exp . El problema con este enfoque es que es menos preciso y que cuesta más que la expresión simple , incluso si no hay un
desbordamiento. Hay otra solución que utiliza controladores de trampa llamados sobre / desbordamiento que cuentan estos dos problemas [Sterbenz 1974].

La idea es la siguiente. Hay un contador global inicializado a cero. Cuando el producto parcial se desborda para algunos k , el controlador de trampas incrementa el
130
contador en uno y devuelve la cantidad desbordada con el exponente envuelto. En la precisión simple IEEE 754, e = 127, entonces si p = 1.45 × 2 , se desbordará
max k
- 62
y hará que se llame al manejador de trampas, lo que envolverá el exponente nuevamente dentro del rango, cambiando p a 1.45 × 2 (ver abajo). Del mismo modo, si p
k
por debajo de los flujos, el contador disminuiría y el exponente negativo quedaría envuelto en uno positivo. Cuando se realizan todas las multiplicaciones, si el contador es
k
cero, el producto final es p . Si el contador es positivo, el producto se desbordó, si el contador es negativo, se desbordó. Si ninguno de los productos parciales está fuera de
n
rango, nunca se llama al manejador de trampas y el cálculo no incurre en ningún costo adicional. Incluso si hay flujos excesivos / insuficientes, el cálculo es más preciso que si
se hubiera calculado con logaritmos, porque cada p se computó a partir de p utilizando una precisión completa multiplicar. Barnett [1987] discute una fórmula donde la
k k-1
precisión total del conteo de desbordamiento / desbordamiento resultó en un error en las tablas anteriores de esa fórmula.
IEEE 754 especifica que cuando se llama a un controlador de trampas de desbordamiento o subdesbordamiento, se pasa el resultado envuelto como un argumento. La
definición de envuelto para desbordamiento es que el resultado se calcula como con una precisión infinita, luego se divide por 2 y luego se redondea a la precisión relevante.
130
Para el subflujo, el resultado se multiplica por 2 . El exponente es 192 para precisión simple y 1536 para precisión doble. Esta es la razón por la cual 1.45 x 2 se
-62
transformó en 1.45 × 2 en el ejemplo anterior.

Modos de redondeo

En el estándar IEEE, el redondeo se produce cuando una operación tiene un resultado que no es exacto, ya que (con la excepción de la conversión decimal binaria) cada
operación se calcula exactamente y luego se redondea. Por defecto, redondear significa redondear hacia el más cercano. El estándar requiere que se proporcionen otros tres
modos de redondeo, a saber, redondear hacia 0, redondear hacia + , y redondear hacia - . Cuando se usa con la operación de conversión a entero, redondear hacia -
hace que el converso se convierta en la función de piso, mientras que redondear hacia + es techo. El modo de redondeo afecta al desbordamiento, ya que cuando el
redondeo hacia 0 o el redondeo hacia - está en efecto, un desbordamiento de magnitud positiva hace que el resultado predeterminado sea el número más grande
representable, no + . De manera similar, los desbordamientos de magnitud negativa producirán el mayor número negativo cuando la ronda hacia + o la ronda hacia 0 esté
en efecto.

Una aplicación de los modos de redondeo ocurre en la aritmética de intervalos (otra se menciona en la conversión de binario a decimal ). Cuando se utiliza aritmética de
intervalos, la suma de dos números x y y es un intervalo , donde se x y redondea hacia - , y es x y redondea hacia + . El resultado exacto de la adición está contenido
21
dentro del intervalo . Sin los modos de redondeo, la aritmética de intervalos generalmente se implementa mediante computación y , donde está la máquina épsilon.
Esto resulta en sobreestimaciones para el tamaño de los intervalos. Dado que el resultado de una operación en aritmética de intervalo es un
intervalo, en general, la entrada a una operación también será un intervalo. Si dos intervalos ,y se añaden,, el resultado es , donde es con el modo de
redondeo establecido para redondear hacia - , y es con el modo de redondeo establecido para redondear hacia + .

Cuando un cálculo de punto flotante se realiza mediante aritmética de intervalos, la respuesta final es un intervalo que contiene el resultado exacto del cálculo. Esto no es
muy útil si el intervalo resulta ser grande (como suele suceder), ya que la respuesta correcta podría estar en cualquier parte de ese intervalo. La aritmética de intervalos tiene
más sentido cuando se utiliza junto con un paquete de punto flotante de precisión múltiple. El cálculo se realiza primero con cierta precisión p . Si la aritmética de intervalos
sugiere que la respuesta final puede ser inexacta, el cálculo se rehace con precisiones cada vez más altas hasta que el intervalo final sea de un tamaño razonable.

Banderas

El estándar IEEE tiene una serie de banderas y modos. Como se mencionó anteriormente, hay un indicador de estado para cada una de las cinco excepciones:
subdesbordamiento, desbordamiento, división por cero, operación no válida e inexacta. Hay cuatro modos de redondeo: redondear hacia el más cercano, redondear hacia +
, redondear hacia 0 y redondear hacia - . Se recomienda encarecidamente que haya un bit de modo de habilitación para cada una de las cinco excepciones. Esta sección
ofrece algunos ejemplos sencillos de cómo estos modos y marcas pueden ponerse en buen uso. Un ejemplo más sofisticado se describe en la sección Conversión de binario a
decimal .
n
Considere escribir una subrutina para calcular x , donde n es un número entero. Cuando n > 0, una rutina simple como

Potencia positiva (x, n) {

mientras (n es par) {

x = x * x

n = n / 2

u = x

while (verdadero) {

n = n / 2

si (n == 0) devuelve u

x = x * x

si (n es impar) u = u * x

n
Si n <0, entonces una forma más precisa de calcular x es no llamar, PositivePower(1/x, -n)sino 1/PositivePower(x, -n)porque la primera expresión multiplica n cantidades, cada
una de las cuales tiene un error de redondeo de la división (es decir, 1 / x ). En la segunda expresión, estos son exactos (es decir, x ), y la división final comete solo un error
de redondeo adicional. Desafortunadamente, esta es una pequeña dificultad en esta estrategia. Si hay PositivePower(x, -n)flujos insuficientes, se llamará al manejador de
- n
trampas de flujo insuficiente o, de lo contrario, se establecerá el indicador de estado de flujo insuficiente. Esto es incorrecto, porque si x n se desborda, entonces x se
22
desbordará o estará dentro del rango. Pero como el estándar IEEE le da al usuario acceso a todas las banderas, la subrutina puede corregir esto fácilmente. Simplemente
desactiva los bits de habilitación de la trampa de desbordamiento y desbordamiento y guarda los bits de estado de desbordamiento y desbordamiento. Entonces calcula
1/PositivePower(x, -n). Si no se establece el bit de estado de desbordamiento ni de desbordamiento, se restauran junto con los bits de habilitación de captura. Si se establece
uno de los bits de estado, restaura los indicadores y rehace el uso del cálculo PositivePower(1/x, -n), lo que hace que se produzcan las excepciones correctas.

Otro ejemplo del uso de indicadores se produce al calcular arccos a través de la fórmula

arccos x = 2 arctan .

Si arctan ( ) evalúa a / 2, entonces arccos (-1) evaluará correctamente a 2 · arctan ( ) = , debido a la aritmética de infinito. Sin embargo, hay un pequeño
inconveniente, porque el cálculo de (1 - x ) / (1 + x ) hará que se establezca el indicador de excepción de división por cero, aunque arccos (-1) no sea excepcional. La solución
a este problema es sencilla. Simplemente guarde el valor de la bandera de división por cero antes de calcular arccos, y luego restaure su valor anterior después del cálculo.

Aspectos de los sistemas


El diseño de casi todos los aspectos de un sistema informático requiere conocimientos sobre el punto flotante. Las arquitecturas de computadora generalmente tienen
instrucciones de punto flotante, los compiladores deben generar esas instrucciones de punto flotante y el sistema operativo debe decidir qué hacer cuando se generan
condiciones de excepción para esas instrucciones de punto flotante. Los diseñadores de sistemas informáticos rara vez obtienen orientación de los textos de análisis numérico,
que generalmente están dirigidos a usuarios y escritores de software, no a diseñadores informáticos. Como ejemplo de cómo las decisiones de diseño plausibles pueden llevar
a un comportamiento inesperado, considere el siguiente programa BASIC.

q = 3.0 / 7.0

si q = 3.0 / 7.0 entonces imprima "Igual":

de lo contrario imprimir "No es igual"

Cuando se compila y ejecuta utilizando el Turbo Basic de Borland en una PC de IBM, el programa se imprime Not Equal. Este ejemplo será analizado en la siguiente sección.

Por cierto, algunas personas piensan que la solución a este tipo de anomalías no es comparar los números de punto flotante por la igualdad, pero en lugar de considerarlos
iguales si están dentro de algún obligado error E . Esto no es una cura para todos porque plantea tantas preguntas como respuestas. ¿Cuál debería ser el valor de E ? Si x <0
y y> 0 están dentro de E , ¿deberían considerarse realmente iguales, aunque tengan signos diferentes? Además, la relación definida por esta regla, a ~ b |a-b|<E,
no es una relación de equivalencia porque a ~ b y b ~ cno implica que a ~ c .

Conjuntos de instrucciones

Es bastante común que un algoritmo requiera una breve ráfaga de mayor precisión para producir resultados precisos. Un ejemplo ocurre en la fórmula cuadrática (
2
) / 2 a . Como se discutió en la sección Prueba del teorema 4 , cuando b 4 ac , el error de redondeo puede contaminar hasta la mitad de los dígitos en las raíces calculadas
2
con la fórmula cuadrática. Al realizar la subcalculación de b - 4 ac en doble precisión, se pierden la mitad de los bits de doble precisión de la raíz, lo que significa que se
conservan todos los bits de precisión simple.
2
El cálculo de b - 4 ac en doble precisión cuando cada una de las cantidades a , b y c son con precisión simple es fácil si hay una instrucción de multiplicación que toma dos
números de precisión simples y produce un resultado de doble precisión. Para producir el producto exactamente redondeado de dos números de p dígitos, un multiplicador
necesita generar la totalidad de 2 pbits de producto, aunque puede tirar bits a medida que avanza. Por lo tanto, el hardware para calcular un producto de doble precisión a
partir de operandos de precisión simple normalmente será solo un poco más caro que un solo multiplicador de precisión, y mucho más barato que un multiplicador de doble
precisión. A pesar de esto, los conjuntos de instrucciones modernos tienden a proporcionar solo instrucciones que producen un resultado de la misma precisión que los
23
operandos.

Si una instrucción que combina dos operandos de precisión simple para producir un producto de doble precisión solo fuera útil para la fórmula cuadrática, no valdría la pena
agregarla a un conjunto de instrucciones. Sin embargo, esta instrucción tiene muchos otros usos. Considere el problema de resolver un sistema de ecuaciones lineales,
a x +a + · · · + a 1n x n = b 1
x
11 1 12 2
a 21 x 1 + a 22 x 2 + · · · + a 2n x n = b 2 · · · a n1 x 1 + a n2 x 2 + · · · + a nn x n = b n

que se puede escribir en forma de matriz como Ax = b , donde

(1)
Supongamos que una solución x se calcula por algún método, tal vez por eliminación gaussiana. Existe una forma sencilla de mejorar la precisión del resultado llamada
mejora iterativa . Primer cálculo
(1)
(12) = Hacha -b

y luego resolver el sistema

(13) Ay =

(1)
Tenga en cuenta que si x es una solución exacta, entonces es el vector cero, como es y . En general, el cálculo de y y incurrirá en un error de redondeo, por lo que Ay
(1) (1) (1)
Axe - b = A(x - x ), donde x es la solución verdadera (desconocida). Entonces y x - x , entonces una estimación mejorada para la solución es
(2) (1)
(14) x =x -y

(1) (2) (2) (3) ( + 1) ( )


Los tres pasos (12) , (13) y (14) se pueden repetir, reemplazando x con x ,yx con x . Este argumento de que x i es más preciso que x i es solo informal.
Para obtener más información, consulte [Golub y Van Loan 1989].

Cuando se realiza una mejora iterativa, es un vector cuyos elementos son la diferencia de los números de punto flotante inexactos cercanos y, por lo tanto, pueden sufrir
(1)
una cancelación catastrófica. Por tanto, la mejora iterativa no es muy útil a menos que = Ax - b se calcule con doble precisión. Una vez más, este es un caso de cálculo
(1)
del producto de dos números de precisión simples ( A y x ), donde se necesita el resultado de la doble precisión total.

Para resumir, las instrucciones que multiplican dos números de punto flotante y devuelven un producto con el doble de precisión que los operandos hacen una adición útil a un
conjunto de instrucciones de punto flotante. Algunas de las implicaciones de esto para los compiladores se discuten en la siguiente sección.

Idiomas y compiladores

La interacción de compiladores y punto flotante se discute en Farnum [1988], y gran parte de la discusión en esta sección se toma de ese documento.

Ambigüedad

Idealmente, una definición de lenguaje debe definir la semántica del lenguaje con la precisión suficiente para probar las afirmaciones sobre los programas. Si bien esto suele
ser cierto para la parte entera de un idioma, las definiciones de idioma a menudo tienen un área gris grande cuando se trata de punto flotante. Quizás esto se deba al hecho
de que muchos diseñadores de idiomas creen que no se puede probar nada sobre el punto flotante, ya que implica un error de redondeo. Si es así, las secciones anteriores
han demostrado la falacia en este razonamiento. En esta sección se analizan algunas áreas grises comunes en las definiciones de idiomas, incluidas sugerencias sobre cómo
tratarlas.

Sorprendentemente, algunos idiomas no especifican claramente que si xes una variable de punto flotante (con un valor de decir 3.0/10.0), entonces cada aparición de
(digamos) 10.0*xdebe tener el mismo valor. Por ejemplo, Ada, que se basa en el modelo de Brown, parece implicar que la aritmética de punto flotante solo tiene que satisfacer
los axiomas de Brown y, por lo tanto, las expresiones pueden tener uno de los muchos valores posibles. Pensar en el punto flotante de esta manera difusa contrasta con el
modelo IEEE, donde se define con precisión el resultado de cada operación de punto flotante. En el modelo IEEE, podemos probar que se (3.0/10.0)*10.0evalúa a 3(Teorema 7).
En el modelo de Brown, no podemos.

Otra ambigüedad en la mayoría de las definiciones de lenguaje se refiere a lo que sucede en casos de desbordamiento, subdesbordamiento y otras excepciones. El estándar
IEEE especifica con precisión el comportamiento de las excepciones, por lo que los idiomas que usan el estándar como modelo pueden evitar cualquier ambigüedad en este
punto.

Otra área gris se refiere a la interpretación de paréntesis. Debido a errores de redondeo, las leyes asociativas del álgebra no se aplican necesariamente a los números de
30 30
punto flotante. Por ejemplo, la expresión (x+y)+ztiene una respuesta totalmente diferente que x+(y+z)cuando x = 10 , y = -10 y z = 1 (es 1 en el primer caso, 0 en el
último). La importancia de preservar los paréntesis no puede ser sobre enfatizada. Los algoritmos presentados en los teoremas 3, 4 y 6 dependen de ello. Por ejemplo, en el
teorema 6, la fórmula x = mx - ( mx - x ) se reduciría a x =x si no fuera por paréntesis, destruyendo así todo el algoritmo. Una definición de idioma que no requiere que se
h h
cumplan los paréntesis es inútil para los cálculos de punto flotante.

La evaluación de subexpresión se define de manera imprecisa en muchos idiomas. Supongamos que dses doble precisión, pero xy yson precisión simple. Luego, en la
expresión, ds + x*y¿el producto se realiza con precisión simple o doble? Otro ejemplo: en x + m/ndonde mynson enteros, ¿es la división una operación de enteros o una coma
flotante? Hay dos formas de abordar este problema, ninguna de las cuales es completamente satisfactoria. El primero es requerir que todas las variables en una expresión
tengan el mismo tipo. Esta es la solución más simple, pero tiene algunos inconvenientes. En primer lugar, los lenguajes como Pascal que tienen tipos de subrango permiten
mezclar variables de subrango con variables enteras, por lo que es algo extraño prohibir mezclar variables de precisión simple y doble. Otro problema concierne a las
constantes. En la expresion0.1*x, la mayoría de los lenguajes interpretan 0.1 como una constante de precisión simple. Ahora suponga que el programador decide cambiar la
declaración de todas las variables de punto flotante de simple a doble precisión. Si 0.1 aún se trata como una constante de precisión simple, entonces habrá un error de
tiempo de compilación. El programador tendrá que buscar y cambiar cada constante de punto flotante.

El segundo enfoque es permitir expresiones mixtas, en cuyo caso se deben proporcionar reglas para la evaluación de subexpresiones. Hay una serie de ejemplos de guía. La
definición original de C requería que cada expresión de punto flotante se computara con doble precisión [Kernighan y Ritchie 1978]. Esto lleva a anomalías como el ejemplo al
principio de esta sección. La expresión 3.0/7.0se calcula con doble precisión, pero si qes una variable de precisión simple, el cociente se redondea a precisión simple para el
almacenamiento. Dado que 3/7 es una fracción binaria que se repite, su valor computado en doble precisión es diferente de su valor almacenado en precisión simple. Así, la
comparación q = 3/7 falla. Esto sugiere que calcular cada expresión con la mayor precisión disponible no es una buena regla.

Otro ejemplo guía son los productos interiores. Si el producto interno tiene miles de términos, el error de redondeo en la suma puede llegar a ser sustancial. Una forma de
reducir este error de redondeo es acumular las sumas con doble precisión (esto se tratará con más detalle en la sección Optimizadores ). Si des una variable de doble
precisión, y x[]y y[]son matrices de precisión simple, entonces el bucle producto interno se verá así d = d + x[i]*y[i]. Si la multiplicación se realiza con precisión simple, se
pierde mucha de la ventaja de la acumulación de precisión doble, ya que el producto se trunca a precisión simple justo antes de agregarse a una variable de precisión doble.

Una regla que cubre los dos ejemplos anteriores es calcular una expresión con la mayor precisión de cualquier variable que ocurra en esa expresión. Luego q = 3.0/7.0se
24
computará completamente con precisión simple y tendrá el valor booleano verdadero, mientras d = d + x[i]*y[i]que se computará con precisión doble, obteniendo la ventaja
completa de la acumulación de precisión doble. Sin embargo, esta regla es demasiado simplista para cubrir todos los casos de manera limpia. Si dxy dyson variables de
precisión doble, la expresión y = x + single(dx-dy)contiene una variable de precisión doble, pero realizar la suma con precisión doble sería inútil, ya que ambos operandos son
precisión simple, como es el resultado.

Una regla de cálculo de subexpresión más sofisticado es el siguiente. Primero asigne a cada operación una precisión tentativa, que es el máximo de las precisiones de sus
operandos. Esta asignación debe realizarse desde las hojas hasta la raíz del árbol de expresión. Luego realice una segunda pasada desde la raíz hasta las hojas. En este pase,
asigne a cada operación el máximo de la precisión tentativa y la precisión esperada por el padre. En el caso de q = 3.0/7.0, cada hoja es de precisión simple, por lo que todas
las operaciones se realizan con precisión simple. En el caso de d = d + x[i]*y[i], la precisión tentativa de la operación de multiplicación es una precisión simple, pero en la
segunda pasada se promueve a precisión doble, ya que su operación principal espera un operando de precisión doble. Y eny = x + single(dx-dy), la adición se realiza en precisión
simple. Farnum [1988] presenta evidencia de que este algoritmo no es difícil de implementar.

La desventaja de esta regla es que la evaluación de una subexpresión depende de la expresión en la que está incrustada. Esto puede tener algunas consecuencias molestas.
Por ejemplo, suponga que está depurando un programa y quiere saber el valor de una subexpresión. No puede simplemente escribir la subexpresión al depurador y pedir que
se evalúe, porque el valor de la subexpresión en el programa depende de la expresión en la que está incrustado. Un comentario final sobre las subexpresiones: dado que la
conversión de constantes decimales en binario es una operación, La regla de evaluación también afecta la interpretación de las constantes decimales. Esto es especialmente
importante para constantes como las 0.1que no son exactamente representables en binario.

Otra área gris potencial ocurre cuando un idioma incluye la exponenciación como una de sus operaciones integradas. A diferencia de las operaciones aritméticas básicas, el
valor de la exponenciación no siempre es obvio [Kahan y Coonen 1982]. Si **es el operador de exponenciación, entonces (-3)**3ciertamente tiene el valor -27. Sin embargo,
3 y ylog
(-3.0)**3.0es problemático. Si el **operador comprueba las potencias enteras, se computaría (-3.0)**3.0como -3.0 = -27. Por otro lado, si la fórmula x = e x se usa
para definir **argumentos reales, entonces, dependiendo de la función de registro, el resultado podría ser un NaN (usando la definición natural de log ( x ) = NaNcuando x<0).
CLOGSin embargo, si se usa la función FORTRAN , entonces la respuesta será -27, porque el estándar ANSI FORTRAN se define CLOG(-3.0)como i + log 3 [ANSI 1978]. El
lenguaje de programación Ada evita este problema definiendo solo la exponenciación para potencias enteras, mientras que ANSI FORTRAN prohíbe elevar un número negativo
a una potencia real.

De hecho, la norma FORTRAN dice que

Cualquier operación aritmética cuyo resultado no esté definido matemáticamente está prohibida ...

Desafortunadamente, con la introducción de ± por el estándar IEEE, el significado de no definido matemáticamente ya no está totalmente definido. Una definición podría ser
b
usar el método que se muestra en la sección Infinito . Por ejemplo, para determinar el valor de una , considere funciones analíticas no constantes f y g con la propiedad de
( ) b
que f ( x ) una y g ( x ) b como x 0. Si f ( x ) g x Siempre se acerca al mismo límite, entonces este debería ser el valor de a . Esta definición establecería 2 = lo
-1
que parece bastante razonable. En el caso de 1.0 , cuando f ( x ) = 1 y g ( x ) = 1 / x el límite se aproxima a 1, pero cuando f ( x ) = 1 - x y g ( x ) = 1 / x el límite es e .
0 ( ) g( ) log ( ) 1 2
Así 1.0 , debería haber un NaN. En el caso de 0 ,f(x) g x =e x f x . Como f y g son analíticos y toman el valor 0 en 0, f ( x ) = a x +a x + ... y g ( x ) =
1 2
1 2 ( ) 0
b x + b x + . .. . Así lim x 0 g ( x ) log f ( x ) = lim x 0 x log ( x ( a 1 + a 2 x + ... )) = lim x 0 x log ( a 1 x ) = 0. Entonces f ( x ) g x e = 1 para todos f y g , lo
1 2
0 25 26 El
que significa que 0 = 1. uso de esta definición definiría de forma inequívoca la función exponencial para todos los argumentos, y en particular definiría
(-3.0)**3.0 ser -27.

El estándar IEEE

La sección El estándar IEEEmientras que el estándar IEEE tiene cuatro precisiones diferentes (aunque las configuraciones recomendadas son single plus single-extended o
single, double y double-extended). El infinito proporciona otro ejemplo. Constantes para representar± podría ser suministrado por una subrutina. Pero eso podría hacerlos
inutilizables en lugares que requieren expresiones constantes, como el inicializador de una variable constante.

Una situación más sutil es manipular el estado asociado con un cálculo, donde el estado consiste en los modos de redondeo, los bits de habilitación de captura, los
controladores de captura y los indicadores de excepción. Un enfoque es proporcionar subrutinas para leer y escribir el estado. Además, una sola llamada que puede establecer
un nuevo valor de forma atómica y devolver el valor anterior suele ser útil. Como muestran los ejemplos en la sección Banderas , un patrón muy común de modificación del
estado IEEE es cambiarlo solo dentro del alcance de un bloque o subrutina. Por lo tanto, la carga recae en el programador para encontrar cada salida del bloque y asegurarse
de que se restaure el estado. El soporte de idioma para establecer el estado precisamente en el alcance de un bloque sería muy útil aquí. Modula-3 es un lenguaje que
implementa esta idea para los manipuladores de trampas [Nelson 1991].
27
Hay una serie de puntos menores que deben considerarse al implementar el estándar IEEE en un idioma. Como x - x = +0 para todos los x , (+0) - (+0) = +0. Sin
embargo, - (+ 0) = -0, por lo tanto - x no debe definirse como 0 - x. La introducción de los NaN puede ser confusa, porque un NaN nunca es igual a ningún otro número
(incluido otro NaN), por lo que x = x ya no es siempre cierto. De hecho, la expresión x x es la forma más sencilla de probar un NaN si no se proporciona la función
recomendada por IEEE . Además, los NaN no están ordenados con respecto a todos los demás números, por lo que x y Isnan no se puede definir como no x > y . Debido a
que la introducción de NaN hace que los números de punto flotante se ordenen parcialmente, una comparefunción que devuelve uno de <, =,> o desordenado puede facilitar que
el programador se ocupe de las comparaciones.

Aunque el estándar IEEE define las operaciones básicas de punto flotante para devolver un NaN si un operando es un NaN, esta podría no ser siempre la mejor definición para
operaciones compuestas. Por ejemplo, cuando se calcula el factor de escala apropiado para usar en el trazado de un gráfico, se debe calcular el máximo de un conjunto de
valores. En este caso, tiene sentido que la operación máxima simplemente ignore los NaN.

Finalmente, el redondeo puede ser un problema. El estándar IEEE define el redondeo con mucha precisión, y depende del valor actual de los modos de redondeo. Esto a veces
entra en conflicto con la definición de redondeo implícito en las conversiones de tipo o la roundfunción explícita en idiomas. Esto significa que los programas que desean utilizar
el redondeo IEEE no pueden usar los primitivos del lenguaje natural, y, a la inversa, los primitivos del lenguaje serán ineficientes para implementar en el número cada vez
mayor de máquinas IEEE.

Optimizadores

Los textos del compilador tienden a ignorar el tema del punto flotante. Por ejemplo, Aho et al. [1986] menciona reemplazar x/2.0con x*0.5, lo que lleva al lector a suponer que
x/10.0debe ser reemplazado por 0.1*x. Sin embargo, estas dos expresiones no tienen la misma semántica en una máquina binaria, porque 0.1 no puede representarse
exactamente en binario. Este libro de texto también sugiere la sustitución x*y-x*zpor x*(y-z), aunque hemos visto que estas dos expresiones pueden tener valores muy
diferentes cuando y z. Aunque califica la afirmación de que se puede usar cualquier identidad algebraica al optimizar el código al observar que los optimizadores no deben
violar la definición del lenguaje, deja la impresión de que la semántica de punto flotante no es muy importante. Ya sea que el estándar de idioma especifique o no que se debe
respetar el paréntesis, (x+y)+zpuede tener una respuesta totalmente diferente a la que se x+(y+z)describe anteriormente. Existe un problema estrechamente relacionado con la
conservación de paréntesis que se ilustra con el siguiente código

eps = 1;

hacer eps = 0.5 * eps; while (eps + 1> 1);

Esto está diseñado para dar una estimación de la máquina épsilon. Si un compilador de optimización nota que eps + 1> 1 eps > 0, el programa se cambiará completamente.
En lugar de calcular el número más pequeño x , de manera que 1 x aún sea mayor que x ( x e ), calculará el número más grande x para el cual x / 2 se redondea a 0 ( x ).
Evitar este tipo de "optimización" es tan importante que vale la pena presentar otro algoritmo muy útil que está totalmente arruinado.

Muchos problemas, como la integración numérica y la solución numérica de ecuaciones diferenciales, involucran el cálculo de sumas con muchos términos. Debido a que cada
adición puede potencialmente introducir un error tan grande como .5 ulp, una suma que involucra miles de términos puede tener un poco de error de redondeo. Una forma
sencilla de corregir esto es almacenar el sumando parcial en una variable de doble precisión y realizar cada adición con doble precisión. Si el cálculo se realiza con precisión
simple, realizar la suma con doble precisión es fácil en la mayoría de los sistemas informáticos. Sin embargo, si el cálculo ya se está realizando con doble precisión, duplicar la
precisión no es tan simple. Un método que a veces se recomienda es ordenar los números y sumarlos de menor a mayor. Sin embargo,

Teorema 8 (fórmula de suma de Kahan)

Supongamos que se calcula utilizando el siguiente algoritmo

S = X [1];

C = 0;

para j = 2 a N {

Y = X [j] - C;

T = S + Y;

C = (T - S) - Y;

S = T;

Entonces la suma calculada S es igual a donde .


Usando la fórmula ingenua , la suma calculada es igual a donde | j | <( n - j ) e . Comparando esto con el error en la fórmula de suma Kahan muestra una mejora
dramática. Cada sumando está perturbado por solo 2 e , en lugar de perturbaciones tan grandes como ne en la fórmula simple. Los detalles están en, Errores en la suma .

Un optimizador que creyó que la aritmética de punto flotante obedecía las leyes del álgebra concluiría que C = [ T - S ] - Y = [( S + Y ) - S ] - Y = 0, haciendo que el
algoritmo sea completamente inútil. Estos ejemplos se pueden resumir diciendo que los optimizadores deben ser extremadamente cautelosos al aplicar identidades
algebraicas que se aplican a los números matemáticos reales a expresiones que involucran variables de punto flotante.

Otra forma en que los optimizadores pueden cambiar la semántica del código de punto flotante involucra constantes. En la expresión 1.0E-40*x, hay un decimal implícito a una
operación de conversión binaria que convierte el número decimal en una constante binaria. Debido a que esta constante no se puede representar exactamente en binario, la
excepción inexacta debe ser provocada. Además, el indicador de subdesbordamiento debe establecerse si la expresión se evalúa con precisión simple. Dado que la constante
es inexacta, su conversión exacta a binario depende del valor actual de los modos de redondeo IEEE. Así un optimizador que convierte1.0E-40Al binario en tiempo de
compilación se cambiaría la semántica del programa. Sin embargo, las constantes como 27.5 que se pueden representar exactamente en la precisión más pequeña disponible
se pueden convertir de forma segura en el momento de la compilación, ya que siempre son exactas, no pueden generar ninguna excepción y no se ven afectadas por los
modos de redondeo. Las constantes que se pretenden convertir en tiempo de compilación deben hacerse con una declaración constante, como por ejemplo const pi = 3.14159265.

La eliminación de subexpresiones comunes es otro ejemplo de una optimización que puede cambiar la semántica de punto flotante, como se ilustra en el siguiente código

C = A * B;

RndMode = arriba

D = A * B;

Aunque A*Bpuede parecer una subexpresión común, no se debe a que el modo de redondeo sea diferente en los dos sitios de evaluación. Tres ejemplos finales: x = x no
puede ser reemplazado por la constante booleana true, porque falla cuando x es un NaN; - x = 0 - x falla para x = +0; y x < y no es lo opuesto a x y , porque los NaN no son
mayores ni menores que los números de punto flotante ordinarios.

A pesar de estos ejemplos, existen optimizaciones útiles que se pueden realizar en el código de punto flotante. En primer lugar, hay identidades algebraicas que son válidas
para los números de punto flotante. Algunos ejemplos en aritmética IEEE son x + y = y + x , 2 × x = x + x , 1 × x = x , y 0.5 × x = x / 2. Sin embargo, incluso estas
identidades simples pueden fallar en algunas máquinas como las supercomputadoras CDC y Cray. La programación de instrucciones y la sustitución de procedimientos en línea
28
son otras dos optimizaciones potencialmente útiles.

Como ejemplo final, considere la expresión dx = x*y, donde xy yson variables de precisión simple, y dxtiene doble precisión. En las máquinas que tienen una instrucción que
multiplica dos números de precisión simple para producir un número de precisión doble, dx = x*ypuede asignarse a esa instrucción, en lugar de compilarse a una serie de
instrucciones que convierten los operandos a doble y luego realizar una multiplicación de doble a doble precisión.

Algunos escritores de compiladores consideran que las restricciones que prohíben la conversión ( x + y ) + z a x + ( y + z ) son irrelevantes, de interés solo para los
programadores que usan trucos no portátiles. Quizás tengan en cuenta que los números de punto flotante modelan los números reales y deberían obedecer las mismas leyes
que los números reales. El problema con la semántica de los números reales es que su implementación es extremadamente costosa. Cada vez que se multiplican dos n bits, el
producto tendrá 2 n bits. Cada vez dos nSe agregan números de bits con exponentes ampliamente espaciados, el número de bits en la suma es n + el espacio entre los
max min max
exponentes. La suma podría tener hasta (e -e ) + n bits, o aproximadamente 2 · e + n bits. Un algoritmo que involucra miles de operaciones (como resolver un
sistema lineal) pronto estará operando en números con muchos bits significativos, y será extremadamente lento. La implementación de funciones de biblioteca tales como
pecado y cos es aún más difícil, porque el valor de estas funciones trascendentales no son números racionales. La aritmética de enteros exacta a menudo es proporcionada
por sistemas lisp y es útil para algunos problemas. Sin embargo, la aritmética de punto flotante exacta rara vez es útil.

El hecho es que hay algoritmos útiles (como la fórmula de suma de Kahan) que explotan el hecho de que ( x + y ) + z x + ( y + z ), y funcionan siempre que el límite

ab=(a+b)(1+)

mantiene (así como límites similares para -, × y /). Dado que estos límites se aplican a casi todo el hardware comercial, sería una tontería para los programadores numéricos
ignorar tales algoritmos, y sería irresponsable para los escritores de compiladores destruir estos algoritmos simulando que las variables de punto flotante tienen semántica de
números reales.

Manejo de excepciones

Los temas tratados hasta ahora tienen que ver principalmente con las implicaciones de precisión y precisión de los sistemas. Los manipuladores de trampas también plantean
algunos problemas de sistemas interesantes. El estándar IEEE recomienda encarecidamente que los usuarios puedan especificar un controlador de trampas para cada una de
las cinco clases de excepciones, y la sección Controladores de trampas proporcionó algunas aplicaciones de los controladores de trampas definidos por el usuario. En el caso
de operaciones inválidas y excepciones de división por cero, el manejador debe contar con los operandos, de lo contrario, con el resultado exactamente redondeado.
Dependiendo del lenguaje de programación que se esté utilizando, el controlador de trampas también podría tener acceso a otras variables en el programa. Para todas las
excepciones, el controlador de trampas debe ser capaz de identificar qué operación se estaba realizando y la precisión de su destino.

El estándar IEEE asume que las operaciones son conceptualmente en serie y que cuando ocurre una interrupción, es posible identificar la operación y sus operandos. En las
máquinas que tienen unidades de tubería o unidades aritméticas múltiples, cuando se produce una excepción, puede que no sea suficiente con que el controlador de trampas
examine el contador del programa. Soporte de hardware para identificar exactamente qué operación atrapada puede ser necesaria.

Otro problema se ilustra con el siguiente fragmento de programa.

x = y * z;

z = x * w;

a = b + c;

d = a / x;

Supongamos que la segunda multiplica una excepción y el manejador de trampas quiere usar el valor de a. En el hardware que puede hacer una suma y una multiplicación en
paralelo, un optimizador probablemente movería la operación de adición antes que la segunda multiplicación, de modo que la adición pueda proceder en paralelo con la
primera multiplicación. Así, cuando la segunda trampa de multiplicación, a = b + cya se ha ejecutado, cambiando potencialmente el resultado dea. No sería razonable que un
compilador evite este tipo de optimización, ya que cada operación de punto flotante puede potencialmente interceptarse y, por lo tanto, se eliminarán prácticamente todas las
optimizaciones de programación de instrucciones. Este problema se puede evitar prohibiendo que los manejadores de trampas accedan directamente a cualquier variable del
programa. En su lugar, el controlador puede recibir los operandos o el resultado como un argumento.

Pero todavía hay problemas. En el fragmento

x = y * z;

z = a + b;

Las dos instrucciones bien podrían ejecutarse en paralelo. Si la trampa de multiplicar, su argumento zya podría haber sido sobrescrito por la adición, especialmente porque la
adición es generalmente más rápida que la multiplicación. Los sistemas informáticos que admiten el estándar IEEE deben proporcionar alguna forma de guardar el valor de z,
ya sea en hardware o haciendo que el compilador evite tal situación en primer lugar.

W. Kahan ha propuesto usar la sustitución previa en lugar de los manipuladores de trampas para evitar estos problemas. En este método, el usuario especifica una excepción
y el valor que desea utilizar como resultado cuando se produce la excepción. Como ejemplo, supongamos que en el código para computar (sin x) / x , el usuario decide que x
= 0 es tan raro que mejoraría el rendimiento para evitar una prueba para x = 0, y en su lugar se encarga de este caso cuando un 0 / 0 trampa se produce. Usando los
manejadores de trampas IEEE, el usuario escribiría un manejador que devuelve un valor de 1 y lo instalaría antes de calcular sin x / x. Al usar la sustitución previa, el usuario
especificaría que cuando se produce una operación no válida, se debe usar el valor 1. Kahan llama a esta presustitución, porque el valor que debe usarse debe especificarse
antes de que se produzca la excepción. Cuando se utilizan manejadores de trampas, el valor a devolver puede calcularse cuando se produce la captura.

29
La ventaja de la sustitución es que tiene una implementación de hardware sencilla. Tan pronto como se haya determinado el tipo de excepción, se puede usar para indexar
una tabla que contiene el resultado deseado de la operación. Si bien la presustitución tiene algunos atributos atractivos, la aceptación generalizada del estándar IEEE hace que
sea poco probable que los fabricantes de hardware lo implementen ampliamente.

Los detalles
En este artículo se han hecho varias afirmaciones sobre las propiedades de la aritmética de punto flotante. Ahora procedemos a mostrar que el punto flotante no es magia
negra, sino que es un tema directo cuyas afirmaciones pueden verificarse matemáticamente. Esta sección está dividida en tres partes. La primera parte presenta una
introducción al análisis de errores y proporciona los detalles para la sección Error de redondeo . La segunda parte explora la conversión de binario a decimal, completando
algunos huecos de la sección El estándar IEEE . La tercera parte analiza la fórmula de suma de Kahan, que se utilizó como ejemplo en la sección Aspectos de sistemas .

Error de redondeo

En la discusión del error de redondeo, se afirmó que un solo dígito de guarda es suficiente para garantizar que la suma y la resta siempre serán precisas (Teorema 2). Ahora
procedemos a verificar este hecho. El teorema 2 tiene dos partes, una para restar y otra para sumar. La parte para la resta es

Teorema 9

Si x e y son números de punto flotante positivos en un formato con parámetros y p, y si la resta se realiza con p + 1 dígitos (es decir, un dígito de guarda), el error de
redondeo relativo en el resultado es menor que

e2e.

Prueba

0
Intercambie x e y si es necesario para que x > y. También es inofensivo para la escala x y y de manera que x se representa por x .x ... x × . Si y se representa como
0 1 p-1
y 0 . y 1 ... y p-1 , entonces la diferencia es exacta. Si y se representa como 0 . y 1 ... y p , luego el dígito de guarda asegura que la diferencia calculada será la diferencia
exacta redondeada a un número de punto flotante, por lo que el error de redondeo es a lo sumo e . En general, sea y = 0.0 ... 0 y ... y p y sea y truncado a p + 1
k+1 k+
dígitos. Entonces
-p-1 -p-2 -p-k
(15) y - <( - 1) ( + + ... + ).

De la definición de dígitos guardia, el valor calculado de x - y es x - redondeado a ser un número de coma flotante, es decir, ( x - )+ , donde el error de redondeo
satisface
-p
(16) | | ( / 2) .

La diferencia exacta es x - y , por lo tanto, el error es ( x - y ) - ( x - + )= -y+ . Hay tres casos. Si x - y 1 entonces el error relativo está limitado por

-p -1 -k -p
(17) [( - 1) ( + ... + ) + / 2] < (1 + / 2) .

En segundo lugar, si x - <1, entonces = 0. Dado que lo más pequeño que puede ser x - y es

-1 -k
>( - 1) ( + ... + ), donde = - 1,

en este caso el error relativo está limitado por

(18) .

El caso final es cuando x - y <1 pero x - 1. La única forma en que esto podría suceder es si x - = 1, en cuyo caso = 0. Pero si = 0, entonces (18) se aplica, de modo
-p -p
que nuevamente el relativo el error está limitado por < (1 + / 2). z

2- 1- 1-2
Cuando = 2, el límite es exactamente 2 e , y este límite se alcanza para x = 1 + 2 p y y = 2 p - 2 p en el límite cuando p . Al agregar números del mismo signo,
no es necesario un dígito de guarda para lograr una buena precisión, como lo muestra el siguiente resultado.

Teorema 10

Si x 0 y y 0 , entonces el error relativo en el cálculo de x + y es como máximo 2 , incluso si no se usan dígitos de guarda.

Prueba

El algoritmo para la suma con k dígitos de guarda es similar al de la resta. Si x y , desplaza y hacia la derecha hasta que los puntos de raíz de x e y estén alineados. Deseche
cualquier dígito desplazado más allá de la posición p + k . Calcule la suma de estos dos números de dígitos p + k exactamente. Luego redondear a p dígitos.
Verificaremos el teorema cuando no se utilicen dígitos de guardia; El caso general es similar. No hay pérdida de generalidad al suponer que x y 0 y que x se escala para que
0 -p+1
tenga la forma d.dd ... d × . En primer lugar, supongamos que no se lleva a cabo. Luego, los dígitos desplazados al final de y tienen un valor menor que , y la suma es
- p +1
al menos 1, por lo que el error relativo es menor que / 1 = 2 e . Si se lleva a cabo, el error de desplazamiento debe agregarse al error de redondeo de

La suma es al menos , por lo que el error relativo es menor que

2 . z

Es obvio que la combinación de estos dos teoremas da el teorema 2. El teorema 2 da el error relativo para realizar una operación. La comparación del error de redondeo de x
2 2
- y y ( x + y ) ( x - y ) requiere conocer el error relativo de múltiples operaciones. El error relativo de x y es 1 = [( x y ) - ( x - y )] / ( x - y ), que satisface | 1 | 2 e . O
escribirlo de otra manera.

(19) x y = ( x - y ) (1 + 1 ), | 1 | 2 e

similar

(20) x y = ( x + y ) (1 + 2 ), | 2 | 2 e

Suponiendo que la multiplicación se realiza calculando el producto exacto y luego redondeando, el error relativo es a lo sumo .5 ulp, por lo que

(21) u v = uv (1 + 3 ), | 3 | mi

para cualquier número de punto flotante u y v . Poner estas tres ecuaciones juntas (dejando que u = x y y v = x y ) da

(22) ( x y) ( x y ) = ( x - y ) (1 + 1 ) ( x + y ) (1 + 2 ) (1 + 3 )

Entonces, el error relativo incurrido al computar ( x - y ) ( x + y ) es

(23)

2
Este error relativo es igual a 1 + 2 + 3 + 1 2 + 1 3 + 2 3 + 1 2 3 , que está delimitado por 5 + 8 . En otras palabras, el error relativo máximo es alrededor de 5 errores de
2
redondeo (ya que e es un número pequeño, e es casi insignificante).

2 2
Un análisis similar de ( x x ) ( y y ) no puede resultar en un valor pequeño para el error relativo, porque cuando dos valores cercanos de x e y se conectan a x -y , el error
relativo generalmente será bastante grande. Otra forma de ver esto es intentar y duplicar el análisis que funcionó en ( x y ) ( x y ), produciendo
2 2 2 2 2
(x x) (y y) = [ x (1 + 1 ) - y (1 + 2 )] (1 + 3 ) = (( x -y ) (1 + 1 ) + ( 1 - 2 ) y ) (1 + 3 )

2 2 2
Cuando x e y están cerca, el término de error ( 1 - 2 ) y puede ser tan grande como el resultado x -y . Estos cálculos justifican formalmente nuestra afirmación de que (
2 2
x - y ) ( x + y ) es más preciso que x -y .
Luego pasamos a un análisis de la fórmula para el área de un triángulo. Para estimar el error máximo que puede ocurrir al calcular con (7) , se necesitará el siguiente hecho.

Teorema 11

Si la resta se realiza con un dígito de guarda e y / 2 x 2y, entonces se calcula exactamente x - y.

Prueba

Tenga en cuenta que si x e y tienen el mismo exponente, entonces x y es exacto. De lo contrario, según la condición del teorema, los exponentes pueden diferir a lo sumo 1.
Escale e intercambie x e y si es necesario para que 0 y x , yx se represente como x 0 . x 1 ... x p - 1 y y como 0. y 1 ... y p . Entonces el algoritmo para calcular x y calculará x - y
exactamente y redondear a un número de punto flotante. Si la diferencia es de la forma 0. d ... d , la diferencia ya tendrá p dígitos, y no es necesario redondear.
1 p
Como x 2 y , x - y y , y como y tiene la forma 0. d 1 ... d p , también lo es x - y . z

Cuando > 2, la hipótesis del teorema 11 no puede ser reemplazada por y / x y ; la condición más fuerte y / 2 x 2 y todavía es necesaria. El análisis del error en ( x - y )
( x + y ), inmediatamente después de la prueba del Teorema 10, utilizó el hecho de que el error relativo en las operaciones básicas de suma y resta es pequeño (es decir, las
ecuaciones (19) y ( 20) ). Este es el tipo más común de análisis de errores. Sin embargo, analizar la fórmula (7) requiere algo más, a saber, el teorema 11, como se muestra
en la siguiente prueba.

Teorema 12

Si la resta usa un dígito de guarda, y si a, b y c son los lados de un triángulo (a b c), entonces el error relativo en el cálculo (a + (b + c)) (c - (a - b)) (c + (a - b)) (a + (b -
c)) es como máximo 16 , siempre que e <.005.

Prueba

Examinemos los factores uno por uno. Del teorema 10, b c = ( b + c ) (1 + 1 ), donde 1 es el error relativo, y | 1 | 2 . Entonces el valor del primer factor es

(a ( b c )) = ( a + ( b c )) (1 + 2 ) = ( a + ( b + c) (1 + 1 )) (1 + 2 ),

y por lo tanto
2 2
( a + b + c ) (1 - 2 ) [ a + ( b + c ) (1 - 2 ) ] · (1-2 ) a ( b c ) [ a + ( b + c ) (1 + 2 )] (1 + 2 ) ( a + b + c ) (1 + 2 )

Esto significa que hay un 1 para que

2
(24) (a (b c)) = (a + b + c) (1 + 1 ) ,|1|2.

El siguiente término implica la resta potencialmente catastrófica de c y a b , porque a b puede tener un error de redondeo. Debido a que a, b y c son los lados de un
triángulo, a b + c , y al combinar esto con el ordenamiento c b a se obtiene un b + c 2 b 2 a . Entonces a - b satisface las condiciones del Teorema 11. Esto significa que a - b
= a b es exacta, por lo tanto, c ( a - b) es una resta inofensiva que se puede estimar a partir del Teorema 9 como

(25) ( c (a b )) = ( c - ( a - b )) (1 + 2 ), | 2 | 2

El tercer término es la suma de dos cantidades positivas exactas, por lo que

(26) ( c (a b )) = (c + ( a - b )) (1 + 3 ), | 3 | 2

Finalmente, el último término es


2
(27) ( a (b c )) = ( a + ( b - c)) (1 + 4 ) ,|4|2,

utilizando tanto el teorema 9 como el teorema 10. Si se supone que la multiplicación es exactamente redondeada, entonces x y = xy (1 + ) con | | , luego combinando (24) ,
(25) , (26) y (27) da

(a (b c)) (c (a b)) (c (a b)) ( a (b c )) ( a + ( b + c)) ( c - ( a - b )) ( c + ( a - b )) (a + (b - c )) E

dónde
2 2
E = (1 + 1 ) (1 + 2 ) (1 + 3 ) (1 + 4 ) (1 + 1 ) (1 + 2 ) (1 + 3 )

6 3 2 2
Un límite superior para E es (1 + 2 ) (1 + ) , que se expande a 1 + 15 +O( ). Algunos escritores simplemente ignoran el término O ( e ), pero es fácil explicarlo.
6 3
Escribir (1 + 2 ) (1 + ) = 1 + 15 + R ( ), R ( ) es un polinomio en e con coeficientes positivos, por lo que es una función creciente de . Como R (.005) = .505, R ( ) <1
6 3
para todos <.005, y por lo tanto E (1 + 2 ) (1 + ) <1 + 16 . Para obtener un límite inferior en E , observe que 1 - 15 - R( ) <E, y así cuando
6 3
<.005, 1 - 16 <(1 - 2 ) (1 - ) . La combinación de estos dos límites produce 1 - 16 < E <1 + 16 . Así, el error relativo es a lo sumo 16 .z

El teorema 12 ciertamente muestra que no hay una cancelación catastrófica en la fórmula (7) . Entonces, aunque no es necesario mostrar que la fórmula (7) es
numéricamente estable, es satisfactorio tener un límite para toda la fórmula, que es lo que da el teorema 3 de cancelación .

Prueba de teorema 3

Dejar
q = ( a + ( b + c )) ( c - ( a - b )) ( c + ( a - b )) ( a + ( b - c ))

Q=(a ( b c ))(c ( a b ))( c ( a b ))( a ( b c )).

Luego, el teorema 12 muestra que Q = q (1 + ), con 16 . Es fácil comprobar que

(28)

2
proporcionado .04 / (. 52) .15, y desde entonces | | 16 16 (.005) = .08, cumple la condición. Así

con | 1 | .52 | | 8.5 . Si las raíces cuadradas se calculan dentro de 0.5 ulp, entonces el error al calcular es (1 + 1 ) (1 + 2 ), con | 2 | . Si = 2, entonces no hay más errores
cometidos al dividir por 4. De lo contrario, un factor más 1 + 3 con | 3 | es necesario para la división, y al usar el método en la prueba del Teorema 12, el límite de error final
de (1 + 1 ) (1 + 2 ) (1 + 3 ) está dominado por 1 + 4 , con | 4 | 11 . z

Para hacer que la explicación heurística inmediatamente después de la declaración del Teorema 4 sea precisa, el siguiente teorema describe qué tan cerca µ ( x ) se aproxima
a una constante.

Teorema 13

Si µ ( x ) = ln (1 + x ) / x , entonces para 0 x , µ ( x ) 1 y la derivada satisface | µ ' ( x ) | .

Prueba

2
Tenga en cuenta que μ ( x ) = 1 - x / 2 + x /3 - ... es una serie alternante con términos decrecientes, por lo que para x 1, μ ( x ) 1-x/2 1/2. Es incluso más fácil ver
que debido a que la serie para µ es alterna, µ ( x ) 1. La serie de Taylor de µ '( x ) también es alterna, y si x tiene términos decrecientes, entonces - µ ' ( x ) - + 2 x
/ 3, o - µ '( x ) 0, por lo tanto | µ '( x ) | . z

Prueba del teorema 4

Desde la serie de Taylor para ln

2/2
es una serie alterna, 0 < x - ln (1 + x ) < x , el error relativo incurrido al aproximar ln (1 + x ) por x está delimitado por x / 2. Si 1 x = 1, entonces | x | < , por lo que el
error relativo está limitado por / 2.
Cuando 1 x 1, defina via 1 x = 1 + . Luego, desde 0 x <1, (1 x ) 1 = . Si la división y los logaritmos se calculan dentro de ulp, entonces el valor computado de la expresión ln
(1 + x ) / ((1 + x ) - 1) es

(29) (1 + 1 ) (1 + 2 ) = (1 + 1 ) (1 + 2 ) = µ ( ) (1 + 1 ) (1 + 2 )

donde | 1 | y | 2 | . Para estimar µ ( ), use el teorema del valor medio, que dice que

(30) µ ( )-µ(x)=( -x)µ'( )

para algunos entre x y . De la definición de , se deduce que | - x | , y combinando esto con el teorema 13 da | µ ( ) - µ ( x ) | / 2, o | µ ( ) / µ ( x ) - 1 | / (2 | µ ( x ) |)
lo que significa que µ ( ) = µ ( x ) (1 + 3 ), con | 3 | . Finalmente, multiplicando por x introduce una final. , por lo que el valor calculado de
4

x · ln (1 x ) / ((1 x ) 1)

es

Es fácil comprobar que si es <0.1, entonces

(1 + 1 ) (1 + 2 ) (1 + 3 ) (1 + 4 ) = 1 + ,

con | | 5 . z

Un ejemplo interesante de análisis de errores usando las fórmulas (19) , (20) y (21) ocurre en la fórmula cuadrática . La sección Cancelación , explicó cómo
reescribir la ecuación eliminará la cancelación potencial causada por la operación ± . Pero hay otra cancelación potencial que puede ocurrir al calcular d = b - 4 ac . Este no
2
2
puede ser eliminado por un simple reordenamiento de la fórmula. En términos generales, cuando b 4 ac, el error de redondeo puede contaminar hasta la mitad de los
dígitos en las raíces calculadas con la fórmula cuadrática. Aquí hay una prueba informal (otro enfoque para estimar el error en la fórmula cuadrática aparece en Kahan
[1972]).

2
Si b 4 ac, el error de redondeo puede contaminar hasta la mitad de los dígitos en las raíces calculadas con la fórmula cuadrática .

2 30 2
Prueba: escriba ( b b) (4 a c ) = ( b (1 + 1 ) - 4 ac (1 + 2 )) (1 + 3 ), donde | i | . Utilizando d = b - 4 ac , esto puede reescribirse como ( d (1 + 1 ) - 4 ac ( 2 - 1 )) (1 +

3
). Para obtener una estimación del tamaño de este error, ignore los términos de segundo orden en i , en cuyo caso el error absoluto es d (1+3)-
2
4ac 4 , donde | 4 | = | 1 - 2 | 2 . Desde , el primer término d ( 1 + 3 ) puede ser ignorada. Para estimar el segundo término, use el hecho de que ax + bx + c = a ( x - r 1 )
2
( x - r 2 ), entonces ar 1 r 2 = c . Desde b 4 ac , entonces r , por lo que el segundo término de error es . Así el valor computado de es
1r2

La desigualdad

muestra que

dónde

-
así que el error absoluto en . Dado que 4 p , y, por tanto, el error absoluto de destruir la mitad inferior de los bits de las raíces r 1 r 2 . En otras palabras,
a se trata
dado que el cálculo de las raíces implica el cálculo con , y esta expresión no tiene bits significativos en la posición correspondiente a la mitad de orden inferior de r i , entonces

los bits de orden inferior de r i no pueden ser significativos. z

Finalmente, pasamos a la prueba del teorema 6. Se basa en el siguiente hecho, que se demuestra en la sección Teorema 14 y Teorema 8 .

Teorema 14

Sea 0 <k <p, y establezca m = k + 1 , y suponga que las operaciones de punto flotante están redondeadas exactamente. Entonces (m x) (m x x) es exactamente igual a x
redondeado a p - k dígitos significativos. Más precisamente, x se redondea tomando el significado de x, imaginando un punto de la raíz justo a la izquierda de los k dígitos
menos significativos y redondeando a un número entero.

Prueba de teorema 6

Por el teorema 14, x es x redondeado a p - k = lugares. Si no se lleva a cabo, entonces x h puede representarse con dígitos significativos. Supongamos que hay una
h
prórroga. Si x = x 0 . x 1 ... x p - 1 × e , luego el redondeo agrega 1 a x p - k - 1 , y la única manera de que se pueda llevar a cabo es si x p - k - 1 = - 1, pero luego dígito de
orden bajo de x h es 1 + x k = 0, y así, de nuevo, x es representable en dígitos.
p- -1 h
-1 p
Para lidiar con x , escale x para que sea un número entero que satisfaga p x - 1. Deje donde están los dígitos de orden alto p - k de x , y son los dígitos de orden inferior
l
k . Hay tres casos a considerar. Si , entonces redondear x a p - k lugares es lo mismo que cortar y , y . Como tiene como máximo k dígitos, si p es par, entonces tiene como
máximo k = = dígitos. De lo contrario, = 2 y Es representable con k - 1 bits significativos. El segundo caso es
k
cuando , y luego el cálculo de x h implica redondear hacia arriba, por lo que x h = + k , y x l = x - x h = x - - = - k . Una vez más, tiene como máximo k dígitos, por lo
-1
que es representable con p / 2 dígitos. Finalmente, si = ( / 2) k , entonces x h = o + k dependiendo de si hay un redondeo hacia
-1 -1 k
arriba. Entonces x l es cualquiera de ( / 2) k o ( / 2) k - k = - / 2, ambos representados con 1 dígito. z

El teorema 6 ofrece una forma de expresar el producto de dos números de precisión de trabajo exactamente como una suma. Hay una fórmula complementaria para expresar
una suma exactamente. Si | x | | y | entonces x + y = ( x y ) + ( x ( x y )) y [Dekker 1971; Knuth 1981, Teorema C en la sección 4.2.2]. Sin embargo, cuando se usan
operaciones exactamente redondeadas, esta fórmula solo es verdadera para = 2, y no para = 10 como en el ejemplo x = .99998, y = .99997 muestra.

Conversión de binario a decimal


24 8
Dado que la precisión simple tiene p = 24 y 2 <10 , puede esperar que la conversión de un número binario a 8 dígitos decimales sea suficiente para recuperar el número
binario original. Sin embargo, éste no es el caso.

Teorema 15
Cuando un número binario de precisión simple IEEE se convierte al número decimal de ocho dígitos más cercano, no siempre es posible recuperar de forma única el número
binario del decimal. Sin embargo, si se usan nueve dígitos decimales, la conversión del número decimal al número binario más cercano recuperará el número de punto
flotante original.

Prueba

3 10
Los números de precisión simple binarios que se encuentran en el intervalo medio abierto [10 ,2 ) = [1000, 1024) tienen 10 bits a la izquierda del punto binario y 14 bits
10 3 14
a la derecha del punto binario. Por lo tanto, hay (2 - 10 )2 = 393,216 números binarios diferentes en ese intervalo. Si los números decimales se representan con 8
10 3 4
dígitos, entonces hay (2 - 10 ) 10 = 240,000 números decimales en el mismo intervalo. No hay forma de que 240,000 números decimales puedan representar 393,216
números binarios diferentes. Por lo tanto, 8 dígitos decimales no son suficientes para representar de manera única cada número binario de precisión única.
Para mostrar que 9 dígitos son suficientes, es suficiente para mostrar que el espaciado entre números binarios siempre es mayor que el espaciado entre números decimales.
Esto asegurará que para cada número decimal N , el intervalo

[N- ulp, N + ulp]

contiene como máximo un número binario. Así, cada número binario se redondea a un número decimal único que a su vez se redondea a un número binario único.
+1
Para mostrar que el espaciado entre los números binarios siempre es mayor que el espaciado entre los números decimales, considere un intervalo [10 n , 10 n ]. En este
( + 1) - 9 n
intervalo, el espaciado entre números decimales consecutivos es 10 n . En [10 n , 2 m ], donde m es el número entero más pequeño, de modo que 10 <2 m , el
- 24 ( + 1) - 9 - 24
espaciado de los números binarios es 2 m , y el espaciado se amplía más adelante en el intervalo. Por tanto, basta con comprobar que 10 n <2 m . Pero, de
( + 1) - 9 -8 -8 -24
hecho, desde el 10 den <2 m , luego 10 n = 10 n 10 <2 m 10 <2 m 2 . z

El mismo argumento aplicado a la precisión doble muestra que se requieren 17 dígitos decimales para recuperar un número de precisión doble.

La conversión binario-decimal también proporciona otro ejemplo del uso de indicadores. Recuerde de la sección Precisión , que para recuperar un número binario de su
| |
expansión decimal, la conversión de decimal a binario debe calcularse exactamente. Esa conversión se realiza multiplicando las cantidades N y 10 P (que son exactos si p
| |
<13) con precisión extendida simple y luego redondeando esto a precisión simple (o dividiendo si p <0; ambos casos son similares). Por supuesto el cálculo de N · 10 P no
| |
puede ser exacto es la ronda de operación combinada ( N · 10 P ) Eso debe ser exacto, donde el redondeo es de precisión simple a extendida. Para ver por qué puede no ser
exacto, tome el caso simple de = 10, p = 2 para single, y p = 3 para single-extended. Si el producto tiene que ser 12.51, esto se redondearía a 12.5 como parte de la
operación de multiplicación de extensión simple. Redondear a una sola precisión daría 12. Pero esa respuesta no es correcta, porque al redondear el producto a una sola
precisión debería dar 13. El error se debe a un doble redondeo.

Al usar las banderas IEEE, se puede evitar el doble redondeo de la siguiente manera. Guarde el valor actual del indicador inexacto y, a continuación, reinícielo. Establezca el
| |
modo de redondeo en redondeado a cero. Luego realiza la multiplicación N · 10 P . Almacene el nuevo valor de la marca inexacta ixflagy restaure el modo de redondeo y la
| | | |
bandera inexacta. Si ixflages 0, entonces N · 10 P es exacto, por lo que la ronda ( N · 10 P ) será correcta hasta el último bit. Si ixflages 1, entonces se truncaron algunos
dígitos, ya que el redondeo a cero siempre se trunca. El significado del producto se verá como 1..b ... b b ... b . Un error de doble redondeo puede producirse si b
1 22 23 31 23
| |
... b = 10 ... 0. Una manera sencilla para dar cuenta de los dos casos es realizar una lógicaORdeixflagcon b . Luego, la ronda ( N · 10 P ) se computará correctamente en
31 31
todos los casos.

Errores en la suma

La sección Optimizadores mencionó el problema de calcular con precisión sumas muy largas. El enfoque más simple para mejorar la precisión es duplicar la precisión. Para
obtener una estimación aproximada de cuánta duplicación de la precisión mejora la precisión de una suma, sea s = x , s = s x 2 ... , s i = s i - 1 x i . Entonces s i = (1
1 1 2 1
+ i ) ( s i - 1 + x i ), donde i , e ignorando los términos de segundo orden en i da

(31)

La primera igualdad de (31) muestra que el valor calculado es el mismo que si se realizara una suma exacta en valores perturbados de x . El primer término x está
j 1
perturbado por n , el último término x solo . La segunda igualdad en (31) muestra que el término de error está delimitado por . Duplicar la precisión tiene el efecto
n
16
de escuadrar . Si la suma se está haciendo en un formato de doble precisión IEEE, 1/10 , por lo que para cualquier valor razonable de n . Así, doblando la precisión toma
la máxima perturbación de n y lo cambia a . Por lo tanto, el límite de 2 errores para la fórmula de suma de Kahan (Teorema 8) no es tan bueno como usar
precisión doble, aunque es mucho mejor que la precisión simple.

Para una explicación intuitiva de por qué funciona la fórmula de suma de Kahan, considere el siguiente diagrama del procedimiento.

Cada vez que se agrega un sumando, hay un factor de corrección C que se aplicará en el siguiente ciclo. Entonces, primero reste la corrección C calculada en el bucle anterior
de X , dando el sumando Y corregido . A continuación, añadir esta sumando a la suma acumulada S . Los bits de orden inferior de Y (es decir, Y ) se pierden en la suma.
j l
Siguiente calcular los bits de orden superior de Y mediante el cálculo de T - S . Cuando Y se resta de esto, los bits de orden inferior de Yserá recuperado. Estos son los bits
que se perdieron en la primera suma en el diagrama. Se convierten en el factor de corrección para el siguiente bucle. Una prueba formal del Teorema 8, tomada de Knuth
[1981] página 572, aparece en la sección Teorema 14 y Teorema 8 ".

Resumen
No es raro que los diseñadores de sistemas informáticos descuiden las partes de un sistema relacionado con el punto flotante. Esto se debe probablemente al hecho de que el
punto flotante recibe muy poca (o ninguna) atención en el currículo de ciencias de la computación. Esto, a su vez, ha provocado la creencia aparentemente generalizada de
que el punto flotante no es un tema cuantificable, por lo que no tiene mucho sentido preocuparse por los detalles del hardware y el software que lo tratan.

Este artículo ha demostrado que es posible razonar rigurosamente sobre el punto flotante. Por ejemplo, se puede probar que los algoritmos de punto flotante que involucran la
cancelación tienen pequeños errores relativos si el hardware subyacente tiene un dígito de guarda, y hay un algoritmo eficiente para la conversión de decimal-binario que
puede demostrarse que es invertible, siempre que la precisión extendida sea soportado. La tarea de construir un software confiable de punto flotante se hace mucho más fácil
cuando el sistema informático subyacente es compatible con el punto flotante. Además de los dos ejemplos que se acaban de mencionar (dígitos de guardia y precisión
extendida), la sección Aspectos de sistemas de este documento tiene ejemplos que van desde el diseño de conjuntos de instrucciones hasta la optimización del compilador
que ilustran cómo soportar mejor el punto flotante.

La aceptación cada vez mayor del estándar de punto flotante IEEE significa que los códigos que utilizan características del estándar son cada vez más portátiles. La sección El
Estándar IEEE , dio numerosos ejemplos que ilustran cómo las características del estándar IEEE se pueden usar para escribir códigos prácticos de punto flotante.

Expresiones de gratitud
Este artículo fue inspirado por un curso impartido por W. Kahan en Sun Microsystems desde mayo hasta julio de 1988, que fue muy bien organizado por David Hough de Sun.
Mi esperanza es permitir que otros aprendan sobre la interacción de los sistemas de punto flotante y de computadora sin tener que levantarse a tiempo para asistir a las
conferencias de las 8:00 am. Gracias a Kahan y a muchos de mis colegas de Xerox PARC (especialmente a John Gilbert) por leer los borradores de este documento y
proporcionar muchos comentarios útiles. Los comentarios de Paul Hilfinger y un árbitro anónimo también ayudaron a mejorar la presentación.

Referencias
Aho, Alfred V., Sethi, R. y Ullman JD 1986. Compiladores: Principios, Técnicas y Herramientas , Addison-Wesley, Reading, MA.

ANSI 1978. Lenguaje de programación estándar nacional estadounidense FORTRAN , ANSI Standard X3.9-1978, Instituto Nacional de Estándares Americanos, New York, NY.

Barnett, David 1987. Un entorno portátil de punto flotante , manuscrito no publicado.

Brown, WS 1981. Un modelo simple pero realista de computación de punto flotante , ACM Trans. en matemáticas. Software 7 (4), pp. 445-480.

Cody, W. J et. Alabama. 1984. Un estándar propuesto para la longitud de la raíz y la palabra para la aritmética de punto flotante , IEEE Micro 4 (4), páginas 86-100.

Cody, WJ 1988. Estándares de punto flotante: teoría y práctica , en "Confiabilidad en la computación: el papel de los métodos de intervalo en la computación científica", ed.
por Ramon E. Moore, pp. 99-107, Academic Press, Boston, MA.

Coonen, Jerome 1984. Contribuciones a un estándar propuesto para la aritmética de punto flotante binario , tesis doctoral, Univ. de california, berkeley.

Dekker, TJ 1971. Una técnica de punto flotante para extender la precisión disponible , número. Mates. 18 (3), pp. 224-242.

Demmel, James 1984. Underflow y la confiabilidad del software numérico , SIAM J. Sci. Stat. Comput. 5 (4), pp. 887-919.

Farnum, Charles 1988. Compilador Compatibilidad para computación de punto flotante , práctica y experiencia de software, 18 (7), pp. 701-709.

Forsythe, GE y Moler, CB 1967. Solución informática de sistemas algebraicos lineales , Prentice-Hall, Englewood Cliffs, NJ.

Goldberg, I. Bennett 1967. 27 bits no son suficientes para una precisión de 8 dígitos , Com. de la ACM. 10 (2), pp 105-106.

Goldberg, David 1990. Aritmética computacional , en "Arquitectura de computadora: un enfoque cuantitativo", por David Patterson y John L. Hennessy, Apéndice A, Morgan
Kaufmann, Los Altos, CA.

Golub, Gene H. y Van Loan, Charles F. 1989. Matrix Computations , 2ª edición, The Johns Hopkins University Press, Baltimore, Maryland.

Graham, Ronald L., Knuth, Donald E. y Patashnik, Oren. 1989. Concrete Mathematics, Addison-Wesley, Reading, MA, p.162.

Hewlett Packard 1982. Manual de funciones avanzadas de HP-15C .

IEEE 1987. Estándar IEEE 754-1985 para aritmética de punto flotante binario , IEEE, (1985). Reimpreso en SIGPLAN 22 (2) pp. 9-25.

Kahan, W. 1972. A Survey Of Error Analysis , en Information Processing 71, Vol. 2, páginas 1214 - 1239 (Ljubljana, Yugoslavia), Holanda del Norte, Ámsterdam.

Kahan, W. 1986. Cálculo del área y el ángulo de un triángulo con forma de aguja , manuscrito no publicado.

Kahan, W. 1987. Cortes de rama para funciones elementales complejas , en "El estado del arte en el análisis numérico", ed. por MJD Powell y A. Iserles (Univ of Birmingham,
Inglaterra), Capítulo 7, Oxford University Press, Nueva York.

Kahan, W. 1988. Conferencias inéditas en Sun Microsystems, Mountain View, CA.

Kahan, W. y Coonen, Jerome T. 1982. La Ortogonalidad de la sintaxis, la semántica y el diagnóstico en entornos de programación numérica , en "La relación entre la
computación numérica y los lenguajes de programación", ed. por JK Reid, pp. 103-115, Holanda Septentrional, Ámsterdam.

Kahan, W. y LeBlanc, E. 1985. Anomalías en el paquete IBM Acrith , Proc. 7mo Simposio IEEE sobre Aritmética Computacional (Urbana, Illinois), pp. 322-331.

Kernighan, Brian W. y Ritchie, Dennis M. 1978. El lenguaje de programación C , Prentice-Hall, Englewood Cliffs, NJ.

Kirchner, R. y Kulisch, U. 1987. Arithmetic for Vector Processors , Proc. 8vo Simposio IEEE sobre Aritmética Computacional (Como, Italia), pp. 256-269.

Knuth, Donald E., 1981. El arte de la programación de computadoras, volumen II , segunda edición, Addison-Wesley, Reading, MA.

Kulisch, UW, y Miranker, WL 1986. La aritmética de la computadora digital: un nuevo enfoque , SIAM Review 28 (1), páginas 1-36.

Matula, DW y Kornerup, P. 1985. Aritmética racional de precisión finita: Sistemas de numeración de barras, Trans. IEEE. en comput. C-34 (1), pp 3-18.

Nelson, G. 1991. Programación de sistemas con Modula-3 , Prentice-Hall, Englewood Cliffs, NJ.

Reiser, John F. y Knuth, Donald E. 1975. Evadir la deriva en la adición de punto flotante , cartas de procesamiento de información 3 (3), páginas 84-87.

Sterbenz, Pat H. 1974. Cálculo de punto flotante , Prentice-Hall, Englewood Cliffs, NJ.

Swartzlander, Earl E. y Alexopoulos, Aristides G. 1975. El sistema numérico de signos / logaritmos , IEEE Trans. Comput. C-24 (12), pp. 1238-1242.

Walther, JS, 1971. Un algoritmo unificado para funciones elementales , Actas de la computadora conjunta Spring Spring Conf. 38, pp. 379-385.

Teorema 14 y Teorema 8.
Esta sección contiene dos de las pruebas más técnicas que se omitieron del texto.

Teorema 14

Sea 0 <k <p, y establezca m = k + 1 , y suponga que las operaciones de punto flotante están redondeadas exactamente. Entonces ( m x ) ( m x x ) es exactamente igual a x
redondeado a p - k dígitos significativos. Más precisamente, x se redondea tomando el significado de x, imaginando un punto de la raíz justo a la izquierda de los k dígitos
menos significativos, y redondeando a un número entero.

Prueba

La prueba se divide en dos casos, dependiendo de si el cálculo de mx = k x + x tiene o no una realización.


Supongamos que no se lleva a cabo. Es inofensivo escalar x para que sea un número entero. Entonces el cálculo de mx = x + k x se ve así:

aa...aabb...bb
+ aa ... aabb ... bb
zz...zzbb...bb

donde x se ha dividido en dos partes. Se marcan los dígitos k de orden bajo by se marcan los dígitos p - k de orden alto a. Calcular m x a partir de mx implica redondear los k
dígitos de orden bajo (los marcados con ) para que b
k
(32) m x = mx - x mod ( k ) + r

El valor de r es 1 si .bb...bes mayor que y 0 en caso contrario. Más precisamente

(33) r = 1 si se a.bb...bredondea a a + 1, r = 0 de lo contrario.

A continuación, calcule m x - x = mx - x mod ( k ) + r k - x = k ( x + r ) - x mod ( k ). La siguiente imagen muestra el cálculo de m x - x redondeado, es decir, ( m x ) x . La línea
superior es k ( x + r ), donde es el dígito que resulta de sumarse al dígito de orden más bajo . Brb

aa...aabb...bB00...00
- bb ... bb
zz... zzZ00...00

Si .bb...b< entonces r = 0, restar provoca un préstamo del dígito marcado B, pero la diferencia se redondea hacia arriba, por lo que el efecto neto es que la diferencia
redondeada es igual a la línea superior, que es k x . Si > entonces r = 1, y 1 se resta de debido al préstamo, entonces el resultado es k x . Finalmente considere el caso = . Si
r = 0 entonces es par, es impar, y la diferencia se redondea hacia arriba, dando k x . Similarmente cuando r = 1, es impar, .bb...b B .bb...b BZ BZes par, la diferencia se
redondea hacia abajo, así que nuevamente la diferencia es k x . Para resumir

(34) ( m x ) x = k x
Combinando las ecuaciones (32) y (34) se obtiene ( m x ) - ( m x x ) = x - x mod ( k ) + · k . El resultado de realizar este cálculo es
r00...00
+ aa...aabb...bb
- bb ... bb
aa...aA00...00

La regla para calcular r , ecuación (33), es la misma que la regla para redondear a lugares p - k . Por lo tanto, calcular mx - ( mx - x ) en la precisión aritmética de punto
k
flotante es exactamente igual al redondeo de x a p - k lugares, en el caso de que x + x no se lleve a cabo.a... ab...b
Cuando x + k x se lleva a cabo, entonces mx = k x + x se ve así:
aa...aabb...bb
+ aa ... aabb ... bb
zz...zZbb...bb

Por lo tanto, m x = mx - x mod ( k ) + w k , donde w = - Z si Z < / 2, pero el valor exacto de w no es importante. A continuación, m x - x = k x - x mod ( k ) + w k . En una foto

aa...aabb...bb00...00
- bb... bb
+ w
zz ... zZbb ...bb31

32
El redondeo da ( m x ) x = k x + w k - r k , donde r = 1 si > o si = y b 0 = 1. Finalmente, .bb...b .bb...b

k
( m x ) - ( m x x) = mx - x mod ( k ) + w - ( k x + w k - r k ) = x - x mod ( k ) + r k .

Y una vez más, r = 1 exactamente cuando se redondea a...ab...ba lugares p - k implica redondear hacia arriba. Así el teorema 14 está probado en todos los casos. z

Teorema 8 (fórmula de suma de Kahan)

Supongamos que se calcula utilizando el siguiente algoritmo

S = X [1];

C = 0;

para j = 2 a N {

Y = X [j] - C;

T = S + Y;

C = (T - S) - Y;

S = T;

Entonces, la suma calculada S es igual a S = x j ( 1 + j ) + O ( N 2 ) | x j |, donde | j | 2 .

Prueba

Primero recuerde cómo fue la estimación del error para la fórmula simple x i . Introduzca s 1 = x 1 , s i = (1 + i ) ( s i - 1 + x i ). Luego, la suma calculada es s n , que es una
suma de términos, cada uno de los cuales es un x i multiplicado por una expresión que involucra j 's. El coeficiente exacto de x 1 es (1 + 2 ) (1 + 3 ) ... (1 + n ), y al
renumerarlo, el coeficiente de x debe ser (1 + 3 ) (1 + 4 ) ... (1 + n ), y así sucesivamente. La prueba del Teorema 8 corre exactamente en las mismas líneas, solo
2
el coeficiente de x 1 es más complicado. En detalle s 0 = c 0 = 0 y

y =x c k - 1 = ( x k - c k - 1 ) (1 + k )
k k
s =s y k = ( s k-1 + y k ) (1 + k )
k k-1
c =(s s k - 1 ) y k = [( s k - s k - 1 ) (1 + k ) - y k ] (1 + k )
k k

donde todas las letras griegas están delimitadas por . Aunque el coeficiente de x en s es la máxima expresión de interés, resulta ser más fácil calcular el coeficiente de x
1 k 1
en s -c yc .
k k k
Cuando k = 1,

c =(s (1 + 1 ) - y 1 ) (1 + d 1 )
1 1
=y ((1 + s ) (1 + 1 ) - 1) (1 + d 1 )
1 1
=x (s + 1 + s 1 g 1 ) (1 + d 1 ) (1 + h 1 )
1 1
s -c = x [(1 + s ) - ( s + g + s g ) (1 + d )] (1 + h )
1 1 1 1 1 1 1 1 1 1
=x [1 - g -s d -s g -d g -s g d ] (1 + h )
1 1 1 1 1 1 1 1 1 1 1 1

Llamar a los coeficientes de x en estas expresiones C yS respectivamente, entonces


1 k k

2
C =2 +O( )
1
2 3
S1=+1-1+4 +O( )

Para obtener la fórmula general para S yC , expanda las definiciones de s yc , ignorando todos los términos que involucran x con i > 1 para obtener
k k k k i

s =(s +y ) (1 + k )
k k-1 k
=[s +(x -c ) (1 + k )] (1 + k )
k-1 k k-1
= [( s -c ) - k c k - 1 ] (1+ k )
k-1 k-1
c = [{ s -s } (1 + k ) - y k ] (1 + k )
k k k-1
= [{(( s -c ) - k c k - 1 ) (1 + k ) - s k - 1 } (1 + k ) + c k - 1 (1 + k )] ( 1 + k )
k-1 k-1
= [{( s -c ) k - k c k -1 (1 + k ) - c k - 1 } (1 + k ) + c k - 1 (1 + k )] (1 + k )
k-1 k-1
= [( s -c ) k (1 + k ) - c k - 1 ( k + k ( k + k + k k ))]] (1 + k ),
k-1 k-1
s -c = (( s -c ) - k c k - 1 ) (1 + k )
k k k-1 k-1
- [( s -c
) (1 + k ) - c k - 1 ( k + k ( k + k + k k )] (1 + k )
k-1 k-1 k
=(s -c ) ((1 + k ) - k (1 + k ) (1 + k ))
k- 1 k-1
+C (- k (1 + k ) + ( k + k ( k + k + k k )) (1 + k ))
k-1
=(s -c ) (1 - k ( k + k + k k ))
-1 k-1
+C -[ k + k + k ( k + k k ) + ( k + k ( k + k + k k )) k ]
k-1

2
Desde S yC solamente se calculan hasta el orden , estas fórmulas pueden simplificarse a
k k

2 2
C =(k+O( )) S k - 1 + (- k + O ( )) C k - 1
k
2 3 2
S = ((1 + 2 +O( )) S k - 1 + (2+( )) C k - 1
k
Usando estas fórmulas da
2
C =2+O( )
2
2 3
S 2 = 1 + 1 - 1 + 10 +O( )

y en general es fácil comprobar por inducción que


2
C =k+O( )
k
2 3
S k = 1 + 1 - 1 + (4 k +2) +O( )

Finalmente, lo que se desea es el coeficiente de x en s . Para obtener este valor, permita que x = 0, que todas las letras griegas con subíndices de n + 1 sean iguales
1 k n+1
2
a 0, y calcule s . Entonces s =s -c , y el coeficiente de x en s es menor que el coeficiente en s , que es S = 1 + 1 - 1 + (4 n + 2) = (1 + 2 + (
n+1 n+1 n n 1 n n+1 n
2
n )). z

Diferencias entre implementaciones de IEEE 754

Nota: esta sección no es parte del artículo publicado. Se ha agregado para aclarar ciertos puntos y corregir posibles ideas erróneas sobre el estándar IEEE que el lector podría
inferir del documento. Este material no fue escrito por David Goldberg, pero aparece aquí con su permiso.

El documento anterior ha demostrado que la aritmética de punto flotante debe implementarse con cuidado, ya que los programadores pueden depender de sus propiedades
para la corrección y precisión de sus programas. En particular, el estándar IEEE requiere una implementación cuidadosa, y es posible escribir programas útiles que funcionen
correctamente y ofrezcan resultados precisos solo en sistemas que cumplan con el estándar. El lector podría estar tentado a concluir que tales programas deberían ser
portátiles a todos los sistemas IEEE. De hecho, el software portátil sería más fácil de escribir si la observación "Cuando un programa se mueve entre dos máquinas y ambas
admiten aritmética IEEE, entonces si cualquier resultado intermedio difiere, debe ser debido a errores de software, no por diferencias en aritmética". cierto.

Desafortunadamente, el estándar IEEE no garantiza que el mismo programa proporcionará resultados idénticos en todos los sistemas conformes. La mayoría de los programas
realmente producirán diferentes resultados en diferentes sistemas por una variedad de razones. Por un lado, la mayoría de los programas involucran la conversión de números
entre formatos decimales y binarios, y el estándar IEEE no especifica completamente la precisión con la que se deben realizar dichas conversiones. Por otro lado, muchos
programas utilizan funciones elementales proporcionadas por una biblioteca del sistema, y el estándar no especifica estas funciones en absoluto. Por supuesto, la mayoría de
los programadores saben que estas características están fuera del alcance del estándar IEEE.

Muchos programadores pueden no darse cuenta de que incluso un programa que usa solo los formatos numéricos y las operaciones prescritas por el estándar IEEE puede
calcular diferentes resultados en diferentes sistemas. De hecho, los autores de la norma pretendían permitir diferentes implementaciones para obtener diferentes resultados.
Su intención es evidente en la definición del término destino.en el estándar IEEE 754: "Un destino puede ser designado explícitamente por el usuario o suministrado
implícitamente por el sistema (por ejemplo, resultados intermedios en subexpresiones o argumentos para procedimientos). Algunos idiomas colocan los resultados de cálculos
intermedios en destinos más allá del usuario No obstante, esta norma define el resultado de una operación en términos del formato de ese destino y los valores de los
operandos ". (IEEE 754-1985, p. 7) En otras palabras, el estándar IEEE requiere que cada resultado se redondee correctamente a la precisión del destino en el que se
colocará, pero el estándar no requiere que la precisión de ese destino sea determinado por el programa de un usuario. Así, diferentes sistemas pueden entregar sus resultados
a destinos con diferentes precisiones,

Varios de los ejemplos del artículo anterior dependen de algunos conocimientos sobre la forma en que se redondea la aritmética de punto flotante. Para poder basarse en
ejemplos como estos, un programador debe poder predecir cómo se interpretará un programa, y en particular, en un sistema IEEE, cuál será la precisión del destino de cada
operación aritmética. Por desgracia, la laguna en la definición de destino del estándar IEEEsocava la capacidad del programador para saber cómo se interpretará un programa.
En consecuencia, es posible que varios de los ejemplos dados anteriormente, cuando se implementan como programas aparentemente portátiles en un lenguaje de alto nivel,
no funcionen correctamente en sistemas IEEE que normalmente entregan resultados a destinos con una precisión diferente de la que espera el programador. Otros ejemplos
pueden funcionar, pero probar que funcionan puede estar más allá de la capacidad del programador promedio.

En esta sección, clasificamos las implementaciones existentes de aritmética IEEE 754 según las precisiones de los formatos de destino que normalmente usan. Luego,
revisamos algunos ejemplos del documento para mostrar que la entrega de resultados con una precisión mayor que la que un programa espera puede ocasionar que se
calculen resultados erróneos, aunque es probablemente correcto cuando se utiliza la precisión esperada. También revisamos una de las pruebas en el documento para ilustrar
el esfuerzo intelectual requerido para hacer frente a una precisión inesperada, incluso cuando no invalida nuestros programas. Estos ejemplos muestran que, a pesar de todo
lo que prescribe el estándar IEEE, las diferencias que permite entre diferentes implementaciones pueden impedirnos escribir software numérico portátil y eficiente cuyo
comportamiento podamos predecir con precisión. Para desarrollar dicho software, entonces,

Implementaciones actuales de IEEE 754

Las implementaciones actuales de la aritmética IEEE 754 se pueden dividir en dos grupos que se distinguen por el grado en que admiten diferentes formatos de punto flotante
en el hardware. Basado en extendidoLos sistemas, ejemplificados por la familia de procesadores Intel x86, brindan soporte completo para un formato extendido de doble
precisión, pero solo soporte parcial para precisión simple y doble: brindan instrucciones para cargar o almacenar datos con precisión simple y doble, convirtiéndolos en el
lugar. vuelan hacia o desde el formato doble extendido, y proporcionan modos especiales (no los predeterminados) en los que los resultados de las operaciones aritméticas se
redondean a precisión simple o doble, aunque se mantienen en registros en formato doble extendido. (Los procesadores de la serie Motorola 68000 redondean los resultados
tanto a la precisión como al rango de los formatos simple o doble en estos modos. Los procesadores Intel x86 y compatibles redondean los resultados a la precisión de los
formatos simples o dobles, pero conservan el mismo rango que el formato doble extendido. ) Individual / dobleLos sistemas, incluidos la mayoría de los procesadores RISC,
brindan soporte completo para formatos de precisión simple y doble, pero no admiten un formato de precisión doble extendida que cumpla con IEEE. (La arquitectura IBM
POWER proporciona solo soporte parcial para precisión simple, pero para el propósito de esta sección, lo clasificamos como un sistema simple / doble).

Para ver cómo un cálculo puede comportarse de manera diferente en un sistema de base extendida que en un sistema simple / doble, considere una versión en C del ejemplo
de la sección Aspectos de sistemas :

int main () {

doble q;

q = 3.0 / 7.0;

if (q == 3.0 / 7.0) printf ("Equal \ n");

else printf ("Not Equal \ n");

devuelve 0;

Aquí, las constantes 3.0 y 7.0 se interpretan como números de coma flotante de doble precisión y la expresión 3.0 / 7.0 hereda el doubletipo de datos. En un sistema simple /
doble, la expresión se evaluará con doble precisión, ya que es el formato más eficiente de usar. Por lo tanto, qse le asigna el valor 3,0 / 7,0 redondeado correctamente a doble
precisión. En la siguiente línea, la expresión 3.0 / 7.0 se evaluará nuevamente con doble precisión y, por supuesto, el resultado será igual al valor que se acaba de asignar q,
por lo que el programa imprimirá "Igual" como se esperaba.

En un sistema de base extendida, aunque la expresión 3.0 / 7.0 tiene tipo double, el cociente se computará en un registro en formato doble extendido y, por lo tanto, en el
modo predeterminado, se redondeará a doble precisión extendida. qSin embargo, cuando el valor resultante se asigna a la variable , puede almacenarse en la memoria y,
como qse declara double, el valor se redondeará a precisión doble. En la siguiente línea, la expresión 3.0 / 7.0 se puede evaluar de nuevo con precisión extendida, lo que arroja
un resultado que difiere del valor de doble precisión almacenado q, lo que hace que el programa imprima "No es igual". Por supuesto, también son posibles otros resultados: el
compilador podría decidir almacenar y así redondear el valor de la expresión 3.0 / 7.0 en la segunda línea antes de compararlo conq, o podría mantenerqEn un registro de
precisión extendida sin almacenarlo. Un compilador optimizado podría evaluar la expresión 3.0 / 7.0 en tiempo de compilación, tal vez con doble precisión o tal vez con doble
precisión extendida. (Con un compilador x86, el programa imprime "Igual" cuando se compila con optimización y "No igual" cuando se compila para la depuración.)
Finalmente, algunos compiladores para sistemas de base extendida cambian automáticamente el modo de precisión de redondeo para hacer que las operaciones produzcan
resultados en los registros ronda de esos resultados con precisión simple o doble, aunque posiblemente con una gama más amplia. Por lo tanto, en estos sistemas, no
podemos predecir el comportamiento del programa simplemente leyendo su código fuente y aplicando una comprensión básica de la aritmética IEEE 754. Tampoco podemos
acusar al hardware o al compilador de no proporcionar un entorno compatible con IEEE 754; El hardware ha entregado un resultado correctamente redondeado a cada
destino, como se requiere, y el compilador ha asignado algunos resultados intermedios a los destinos que están fuera del control del usuario, como se le permite.

Errores en la computación en sistemas de base extendida

La sabiduría convencional sostiene que los sistemas de base extendida deben producir resultados que sean al menos tan precisos, si no más precisos que los entregados en
sistemas simples / dobles, ya que los primeros siempre proporcionan al menos tanta precisión y, a menudo, más que los últimos. Los ejemplos triviales, como el programa C
anterior, así como los programas más sutiles basados en los ejemplos que se analizan a continuación, muestran que esta sabiduría es, en el mejor de los casos, ingenua:
algunos programas aparentemente portátiles, que de hecho son portátiles en sistemas simples / dobles, brindan resultados incorrectos en la extensión sistemas basados
precisamente porque el compilador y el hardware conspiran para proporcionar ocasionalmente más precisión de lo que el programa espera.

Los lenguajes de programación actuales dificultan que un programa especifique la precisión que espera. Como se menciona en la sección Idiomas y compiladores , muchos
lenguajes de programación no especifican que cada aparición de una expresión como 10.0*xen el mismo contexto se evalúe con el mismo valor. Algunos idiomas, como Ada, se
vieron influenciados a este respecto por las variaciones entre las diferentes aritméticas anteriores al estándar IEEE. Más recientemente, los lenguajes como ANSI C se han
visto influenciados por los sistemas de base extendida que cumplen con los estándares. De hecho, el estándar ANSI C permite explícitamente a un compilador evaluar una
expresión de punto flotante con una precisión más amplia que la que normalmente se asocia con su tipo. Como resultado, el valor de la expresión10.0*xpuede variar de
maneras que dependen de una variedad de factores: si la expresión se asigna inmediatamente a una variable o aparece como una subexpresión en una expresión más
grande; si la expresión participa en una comparación; si la expresión se pasa como un argumento a una función, y si es así, si el argumento se pasa por valor o por
referencia; el modo de precisión actual; el nivel de optimización en el que se compiló el programa; el modo de precisión y el método de evaluación de expresiones utilizado
por el compilador cuando se compiló el programa; y así.

Los estándares de lenguaje no son del todo responsables de los caprichos de la evaluación de la expresión. Los sistemas de base extendida se ejecutan más eficientemente
cuando las expresiones se evalúan en registros de precisión extendida siempre que sea posible, pero los valores que deben almacenarse se almacenan con la precisión más
estrecha requerida. Restringir un lenguaje para exigir que se 10.0*xevalúe con el mismo valor en todas partes impondría una penalización de rendimiento en esos sistemas.
Desafortunadamente, permitir que esos sistemas evalúen de manera 10.0*xdiferente en contextos sintácticamente equivalentes impone una penalización propia a los
programadores de software numérico preciso al evitar que confíen en la sintaxis de sus programas para expresar su semántica prevista.

¿Los programas reales dependen del supuesto de que una expresión dada siempre se evalúa con el mismo valor? Recuerde el algoritmo presentado en el teorema 4 para
calcular ln (1 + x ), escrito aquí en Fortran:

función real log1p (x)

x real

si (1.0 + x .eq. 1.0) entonces

log1p = x

más

log1p = log (1.0 + x) * x / ((1.0 + x) - 1.0)

terminara si

regreso

En un sistema de base extendida, un compilador puede evaluar la expresión 1.0 + xen la tercera línea con precisión extendida y comparar el resultado con 1.0. Sin embargo,
cuando la misma expresión se pasa a la función de registro en la sexta línea, el compilador puede almacenar su valor en la memoria, redondeando a una sola precisión. Por lo
tanto, si xno es tan pequeño que 1.0 + xredondea 1.0en precisión extendida pero lo suficientemente pequeño como 1.0 + xpara redondeado 1.0en precisión simple, entonces el
valor devuelto log1p(x)será cero en lugar de x, y el error relativo será uno, bastante mayor que 5 . Del mismo modo, suponga que el resto de la expresión en la sexta línea,
incluida la repetición de la subexpresión 1.0 + x, se evalúa con precisión extendida. En ese caso, sixes pequeño pero no lo suficientemente pequeño como 1.0 + xpara
redondearse a una 1.0sola precisión, entonces el valor devuelto por log1p(x)puede exceder el valor correcto casi tanto como x, y nuevamente el error relativo puede
-24 -47
aproximarse a uno. Para un ejemplo concreto, tome x2 +2 , por lo que xes el número de precisión simple más pequeño, que se 1.0 + xredondea al siguiente número
-23 -23
más grande, 1 + 2 . Entonces log(1.0 + x)es aproximadamente 2 . Debido a que el denominador en la expresión en la sexta línea se evalúa con precisión extendida, se
-23
calcula exactamente y se entrega x, por lo que log1p(x)devuelve aproximadamente 2 , que es casi el doble de grande que el valor exacto. (Esto sucede en realidad con al
menos un compilador. Cuando el compilador de Sun WorkShop 4.2.1 compila el código anterior para los sistemas x86 que utilizan el -Oindicador de optimización, el código
generado se calcula 1.0 + xexactamente como se describe. Como resultado, la función ofrece cero por log1p(1.0e-10)y 1.19209E-07por log1p(5.97e-8))

Para que el algoritmo del teorema 4 funcione correctamente, la expresión 1.0 + xdebe evaluarse de la misma manera cada vez que aparece; el algoritmo puede fallar en
sistemas basados en la extensión solo cuando 1.0 + xse evalúa con precisión doble extendida en una instancia y con precisión simple o doble en otra. Por supuesto, dado que
loges una función intrínseca genérica en Fortran, un compilador podría evaluar la expresión1.0 + xen precisión extendida, calculando su logaritmo con la misma precisión, pero
evidentemente no podemos asumir que el compilador lo hará. (También se puede imaginar un ejemplo similar que involucre una función definida por el usuario. En ese caso,
un compilador podría mantener el argumento con precisión extendida aunque la función devuelva un resultado de precisión simple, pero pocos compiladores Fortran
existentes, si los hay, pueden hacer esto. .) Por lo tanto, podríamos intentar asegurarnos de que 1.0 + xse evalúe de manera consistente asignándola a una variable.
Desafortunadamente, si declaramos esa variablereal, es posible que un compilador que nos sustituye un valor que se mantiene en un registro con una precisión extendida
todavía nos pueda frustrar por una apariencia de la variable y un valor almacenado en la memoria con una sola precisión por otra. En su lugar, deberíamos declarar la variable
con un tipo que corresponda al formato de precisión extendida. El estándar FORTRAN 77 no proporciona una manera de hacer esto y, si bien Fortran 95 ofrece el
SELECTED_REAL_KINDmecanismo para describir varios formatos, no requiere explícitamente implementaciones que evalúen expresiones con precisión extendida para permitir que
las variables se declaren con esa precisión. En resumen, no hay una forma portátil de escribir este programa en Fortran estándar que garantice que se evite que la expresión
1.0 + xse evalúe de una manera que invalida nuestra prueba.

Hay otros ejemplos que pueden funcionar incorrectamente en sistemas basados en la extensión incluso cuando cada subexpresión se almacena y, por lo tanto, se redondea a
la misma precisión. La causa es el doble redondeo.. En el modo de precisión predeterminado, un sistema de base extendida inicialmente redondeará cada resultado a doble
precisión extendida. Si ese resultado se almacena con doble precisión, se redondea nuevamente. La combinación de estos dos redondeos puede producir un valor diferente al
que se habría obtenido al redondear el primer resultado correctamente a doble precisión. Esto puede suceder cuando el resultado redondeado a doble precisión extendida es
un "caso intermedio", es decir, se encuentra exactamente a la mitad entre dos números de doble precisión, por lo que el segundo redondeo está determinado por la regla de
redondeo de empates a pares. Si este segundo redondeo redondea en la misma dirección que el primero, el error de redondeo neto excederá la mitad de una unidad en el
último lugar. (Tenga en cuenta, sin embargo, que el doble redondeo solo afecta a los cálculos de doble precisión. Uno puede probar que la suma, la diferencia,Los números p-
bit, o la raíz cuadrada de un p- bit número, redondeados primero a q bits y luego a p bits dan el mismo valor que si el resultado se redondeara solo una vez a p bits provistos
q 2 p + 2. Por lo tanto, la precisión doble extendida es lo suficientemente amplia como para que los cálculos de precisión simple no sufran doble redondeo.)

Algunos algoritmos que dependen del redondeo correcto pueden fallar con el redondeo doble. De hecho, incluso algunos algoritmos que no requieren un redondeo correcto y
funcionan correctamente en una variedad de máquinas que no cumplen con IEEE 754 pueden fallar con el doble redondeo. El más útil de estos son los algoritmos portátiles
para realizar aritmética de precisión múltiple simulada que se mencionan en la sección Operaciones Exactamente Redondeadas . Por ejemplo, el procedimiento descrito en el
teorema 6 para dividir un número de coma flotante en partes altas y bajas no funciona correctamente en aritmética de redondeo doble: intente dividir el número de precisión
52 26
doble 2 +3 × 2 - 1 en dos partes, cada una con un máximo de 26 bits. Cuando cada operación se redondea correctamente a precisión doble, la parte de orden superior
52 27 26-1
es 2 +2 y la parte de orden baja es 2 , pero cuando cada operación se redondea primero a precisión doble extendida y luego a precisión doble, el procedimiento
52 28 26
produce una parte de alto orden de 2 +2 y una parte de bajo orden de -2 - 1. El último número ocupa 27 bits, por lo que su cuadrado no se puede calcular
exactamente con doble precisión. Por supuesto, aún sería posible calcular el cuadrado de este número con doble precisión extendida, pero el algoritmo resultante ya no sería
portátil a sistemas simples / dobles. Además, los pasos posteriores en el algoritmo de multiplicación de precisión múltiple asumen que todos los productos parciales se han
computado con doble precisión. Manejar una mezcla de variables dobles dobles y extendidas correctamente haría la implementación significativamente más costosa.

Del mismo modo, los algoritmos portátiles para agregar múltiples números de precisión representados como matrices de números de doble precisión pueden fallar en la
aritmética de doble redondeo. Estos algoritmos generalmente se basan en una técnica similar a la fórmula de suma de Kahan. Como lo sugiere la explicación informal de la
fórmula de suma dada en Errores en la suma , si sy yson variables de punto flotante con | s| | y| y nosotros calculamos:

t = s + y;

e = (s - t) + y;

luego, en la mayoría de las aritméticas, erecupera exactamente el error de redondeo que ocurrió en la computación t. Sin embargo, esta técnica no funciona en aritmética de
52 -54 52 52
doble redondeo: si s= 2 + 1 y y= 1/2 - 2 , luego s + yredondea primero a 2 + 3/2 en doble precisión extendida, y este valor se redondea a 2 + 2 en doble precisión
-54.
según la regla de lazos redondos a pares; por lo tanto, el error de redondeo neto en computación tes 1/2 + 2 , que no se puede representar con precisión doble y, por lo
tanto, no se puede calcular exactamente con la expresión que se muestra arriba. Aquí nuevamente, sería posible recuperar el error de redondeo calculando la suma en doble
precisión extendida, pero luego un programa tendría que hacer un trabajo extra para reducir las salidas finales a doble precisión, y el doble redondeo podría afectar este
proceso, también. Por esta razón, aunque los programas portátiles para simular aritmética de precisión múltiple mediante estos métodos funcionan de manera correcta y
eficiente en una amplia variedad de máquinas, no funcionan como se anuncia en los sistemas de base extendida.

Finalmente, algunos algoritmos que a primera vista parecen depender del redondeo correcto pueden, de hecho, funcionar correctamente con el redondeo doble. En estos
casos, el costo de hacer frente al doble redondeo no radica en la implementación sino en la verificación de que el algoritmo funciona como se anuncia. Para ilustrar, probamos
la siguiente variante del Teorema 7:

Teorema 7 '

52
Si m y n son números enteros representables en el estándar IEEE 754 de doble precisión con | m | < 2 y n tiene la forma especial n = 2 i + 2 j , luego (m n) n = m,
siempre que ambas operaciones de punto flotante estén bien redondeadas correctamente a doble precisión o redondeadas primero a doble precisión extendida y luego a doble
precisión.

Prueba

52 53
Supongamos sin pérdida que m > 0. Sea q = m n . La ampliación por potencias de dos, se puede considerar un ajuste equivalente en el que 2 m <2 y lo mismo para q
, de modo que tanto m y q son números enteros cuyos bits menos significativos ocupan el lugar de unidades (es decir, ULP ( m ) = ULP ( q ) = 1). Antes de escalar, asumimos
52
que m <2 , así que después de escalar, m es un número entero par. También, debido a que los valores escalados de m y q satisface determinados m / 2 < q <2 m , el
valor correspondiente de n debe tener una de dos formas dependiendo de cuál de m o q es mayor: si q < m , entonces evidentemente 1 < n <2, y dado que n es una suma
- -( + 1)
de dos potencias de dos , n = 1 + 2 k para algunos k ; de manera similar, si q > m , entonces 1/2 < n <1, entonces n = 1/2 + 2 k . (Como n es la suma de dos
-52 -52
potencias de dos, el valor más cercano posible de na uno es n = 1 + 2 . Debido a que m / (1 + 2 ) no es mayor que el siguiente número de precisión doble menor que
m , no podemos tener q = m .)

Digamos que e denota el error de redondeo al calcular q , de modo que q = m / n + e , y el valor computado q n será el valor redondeado (una o dos veces) de m + ne .
-(
Considere primero el caso en el que cada operación de punto flotante se redondea correctamente a la precisión doble. En este caso, | e | <1/2. Si n tiene la forma 1/2 + 2
-( + 2)
k + 1) , entonces ne = nq - m es un múltiplo entero de 2
- ( k + 1)
y | ne | <1/4 + 2 k . Esto implica que | ne | 1/4. Recuerde que la diferencia entre my el siguiente
52 52
número representable más grande es 1 y la diferencia entre my el siguiente número representable más pequeño es 1 si m > 2 o 1/2 si m = 2 . Así, como | ne | 1/4, m
52 -
+ ne redondeará a m . (Incluso si m = 2 y ne = -1/4, el producto se redondeará a mpor la regla de lazos redondos a pares.) De manera similar, si n tiene la forma 1 + 2 k
- -( + 1) 52
, entonces ne es un múltiplo entero de 2 k y | ne | <1/2 + 2 k ; esto implica | ne | 1/2. No podemos tener m = 2 en este caso porque m es estrictamente mayor
que q , por lo que m se diferencia de sus vecinos representables más cercanos en ± 1. Por lo tanto, como | ne | 1/2, de nuevo m + ne redondeará am . (Incluso si | ne | =
1/2, el producto se redondeará a m mediante la regla round-ties-to-even porque m es par.) Esto completa la prueba para la aritmética redondeada correctamente.

En aritmética de doble redondeo, todavía puede suceder que q sea el cociente correctamente redondeado (aunque en realidad fue redondeado dos veces), por lo que | e |
<1/2 como arriba. En este caso, podemos apelar a los argumentos del párrafo anterior siempre que consideremos el hecho de que q n se redondeará dos veces. Para tener en
cuenta esto, tenga en cuenta que el estándar IEEE requiere que un formato doble extendido lleve al menos 64 bits significativos, de modo que los números m ± 1/2 ym ± 1/4
- ( k + 1)
puedan representarse exactamente en doble precisión extendida. Por lo tanto, si n tiene la forma 1/2 + 2 , de modo que | ne | 1/4, luego redondeando m + ne a
doble precisión extendida debe producir un resultado que difiera de m como máximo 1/4, y como se indicó anteriormente, este valor se redondeará a m con doble precisión.
-
De manera similar, si n tiene la forma 1 + 2 k , de modo que | ne | 1/2, luego redondeando m + ne a la precisión doble extendida debe producir un resultado que difiera
52
de m como máximo 1/2, y este valor se redondeará a m con doble precisión. (Recuerde que m > 2 en este caso).
-( +
Finalmente, debemos considerar los casos en los que q no es el cociente correctamente redondeado debido al doble redondeo. En estos casos, tenemos | e | <1/2 + 2 d
1)
en el peor de los casos, donde d es el número de bits adicionales en el formato doble extendido. (Todos los sistemas de base extendida existentes admiten un formato
doble extendido con exactamente 64 bits significativos; para este formato, d = 64 - 53 = 11.) Debido a que el doble redondeo solo produce un resultado incorrectamente
-( + 1)
redondeado cuando el segundo redondeo está determinado por la ronda La regla de -ties-to-even, q debe ser un entero par. Así, si n tiene la forma 1/2 + 2 k , entonces
-
ne = nq - m es un múltiplo entero de 2 k ,y
-( + 1) -( + 1) -( + 2) -( + 2) -( + + 2)
| ne | <(1/2 + 2 k ) (1/2 + 2 d ) = 1/4 + 2 k +2 d +2 k d .

- ( d + 2)
Si k d , esto implica | ne | 1/4. Si k > d , tenemos | ne | 1/4 + 2 . En cualquier caso, el primer redondeo del producto entregará un resultado que difiere de m como
-k
máximo 1/4, y según los argumentos anteriores, el segundo redondeo redondeará a m . De manera similar, si n tiene la forma 1 + 2 , entonces ne es un múltiplo entero de
- ( k - 1)
2 ,y
-( + 1) -( + 1) -( + + 1)
| ne | <1/2 + 2 k +2 d +2 k d .

d -( + 1)
Si k d , esto implica | ne | 1/2. Si k > d , tenemos | ne | 1/2 + 2 . En cualquier caso, el primer redondeo del producto entregará un resultado que difiere de m como
máximo 1/2, y nuevamente, según los argumentos anteriores, el segundo redondeo redondeará a m . z

La prueba anterior muestra que el producto puede incurrir en redondeo doble solo si el cociente sí lo hace, y aún así, se redondea al resultado correcto. La prueba también
muestra que extender nuestro razonamiento para incluir la posibilidad de doble redondeo puede ser desafiante incluso para un programa con solo dos operaciones de punto
flotante. Para un programa más complicado, puede ser imposible tener en cuenta de manera sistemática los efectos del doble redondeo, sin mencionar las combinaciones más
generales de los cálculos de precisión doble doble y extendida.

Soporte de lenguaje de programación para precisión extendida

Los ejemplos anteriores no deben tomarse para sugerir que la precisión extendida per se es perjudicial. Muchos programas pueden beneficiarse de la precisión extendida
cuando el programador puede usarlo de forma selectiva. Desafortunadamente, los lenguajes de programación actuales no proporcionan medios suficientes para que un
programador especifique cuándo y cómo debe usarse la precisión extendida. Para indicar qué soporte se necesita, consideramos las formas en que podríamos querer
administrar el uso de la precisión extendida.

En un programa portátil que utiliza la precisión doble como su precisión de trabajo nominal, hay cinco formas en que podemos querer controlar el uso de una precisión más
amplia:

1. Compile para producir el código más rápido, utilizando precisión extendida cuando sea posible en sistemas basados en extensión. Claramente, la mayoría del software
numérico no requiere más aritmética que el error relativo en cada operación está limitado por la "máquina épsilon". Cuando los datos en la memoria se almacenan con
doble precisión, la máquina épsilon generalmente se considera el error de redondeo relativo más grande en esa precisión, ya que se supone que los datos de entrada se
redondearon (correcta o incorrectamente) cuando se ingresaron y los resultados Igualmente se redondearán cuando se almacenen. Por lo tanto, si bien el cálculo de
algunos de los resultados intermedios en precisión extendida puede producir un resultado más preciso, la precisión extendida no es esencial. En este caso,

2. Use un formato más ancho que el doble si es razonablemente rápido y lo suficientemente ancho, de lo contrario, recurra a otra cosa. Algunos cálculos se pueden realizar
con mayor facilidad cuando se dispone de precisión extendida, pero también se pueden realizar con doble precisión con un esfuerzo algo mayor. Considere calcular la
norma euclidiana de un vector de números de doble precisión. Al calcular los cuadrados de los elementos y acumular su suma en un formato doble IEEE 754 extendido
con su rango de exponente más amplio, podemos evitar trivialmente el desbordamiento o el desbordamiento prematuro para vectores de longitudes prácticas. En
sistemas de base extendida, esta es la forma más rápida de calcular la norma. En sistemas simples / dobles, un formato doble extendido tendría que ser emulado en el
software (si se admitiera alguno), y tal emulación sería mucho más lenta que simplemente usar la doble precisión. probar los indicadores de excepción para determinar si
se produjo un desbordamiento o un desbordamiento, y si es así, repetir el cálculo con una escala explícita. Tenga en cuenta que para admitir este uso de precisión
extendida, un lenguaje debe proporcionar una indicación del formato más amplio disponible que sea razonablemente rápido, de modo que un programa pueda elegir qué
método usar, y los parámetros ambientales que indican la precisión y el rango de cada formato. , para que el programa pueda verificar que el formato rápido más ancho
es lo suficientemente ancho (por ejemplo, que tiene un rango más amplio que el doble).

3. Use un formato más ancho que el doble, incluso si tiene que ser emulado en el software. Para programas más complicados que el ejemplo de la norma euclidiana, el
programador puede simplemente desear evitar la necesidad de escribir dos versiones del programa y, en cambio, confiar en la precisión extendida, incluso si es lenta.
Nuevamente, el idioma debe proporcionar parámetros ambientales para que el programa pueda determinar el rango y la precisión del formato más amplio disponible.

4. No utilice una precisión más amplio; redondear los resultados correctamente a la precisión del formato doble, aunque posiblemente con un rango extendido. Para que los
programas que se escriben con mayor facilidad dependan de una aritmética de doble precisión correctamente redondeada, incluidos algunos de los ejemplos
mencionados anteriormente, un lenguaje debe proporcionar al programador una manera de indicar que no se debe usar una precisión extendida, incluso aunque se
puedan calcular resultados intermedios en registros con un rango de exponente más amplio que el doble. (Los resultados intermedios calculados de esta manera aún
pueden incurrir en doble redondeo si se desbordan cuando se almacenan en la memoria: si el resultado de una operación aritmética se redondea primero a 53 bits
significativos, luego se redondea nuevamente a menos bits significativos cuando se debe desnormalizar, el resultado final puede diferir de lo que se hubiera obtenido al
redondear solo una vez a un número desnormalizado. Por supuesto, es muy poco probable que esta forma de doble redondeo afecte negativamente a cualquier programa
práctico.)

5. Redondea los resultados correctamente a la precisión y al rango del formato doble. Esta aplicación estricta de la doble precisión sería más útil para los programas que
prueban el software numérico o la propia aritmética cerca de los límites tanto del rango como de la precisión del formato doble. Tales programas de prueba cuidadosos
tienden a ser difíciles de escribir de manera portátil; se vuelven aún más difíciles (y propensos a errores) cuando deben emplear subrutinas ficticias y otros trucos para
obligar a los resultados a redondearse a un formato particular. Por lo tanto, un programador que utiliza un sistema de base extendida para desarrollar un software
robusto que debe ser portátil para todas las implementaciones de IEEE 754 rápidamente podría apreciar la capacidad de emular la aritmética de sistemas simples /
dobles sin un esfuerzo extraordinario.

Ningún idioma actual es compatible con las cinco de estas opciones. De hecho, pocos idiomas han intentado dar al programador la capacidad de controlar el uso de la
precisión extendida. Una excepción notable es el ISO / IEC 9899: 1999 Lenguajes de programación - estándar C, la última revisión al lenguaje C, que ahora se encuentra en
las etapas finales de la estandarización.

El estándar C99 permite que una implementación evalúe expresiones en un formato más amplio que el que normalmente se asocia con su tipo, pero el estándar C99
recomienda usar uno de solo tres métodos de evaluación de expresiones. Los tres métodos recomendados se caracterizan por la medida en que las expresiones se
"promocionan" a formatos más amplios, y se recomienda a la implementación que identifique qué método utiliza al definir la macro del preprocesador FLT_EVAL_METHOD: si
FLT_EVAL_METHODes 0, cada expresión se evalúa en un formato que corresponde a su tipo; si FLT_EVAL_METHODes 1, las floatexpresiones se promueven al formato que corresponde a
double; y si FLT_EVAL_METHODes 2, floaty las doubleexpresiones se promueven al formato que corresponde a long double. (Se permite una implementación para
establecerFLT_EVAL_METHODa -1 para indicar que el método de evaluación de expresiones es indeterminable.) El estándar C99 también requiere que el <math.h>archivo de
encabezado defina los tipos float_ty double_t, que son al menos tan anchos como floaty double, respectivamente, y están destinados a coincidir con los tipos utilizados para
evaluar floaty doubleexpresiones Por ejemplo, si FLT_EVAL_METHODes 2, ambos float_ty double_tson long double. Finalmente, el estándar C99 requiere que el <float.h>archivo de
encabezado defina macros de preprocesador que especifiquen el rango y la precisión de los formatos correspondientes a cada tipo de punto flotante.
La combinación de funciones requeridas o recomendadas por el estándar C99 admite algunas de las cinco opciones enumeradas anteriormente, pero no todas. Por ejemplo, si
una implementación asigna el long doubletipo a un formato doble extendido y define FLT_EVAL_METHODque es 2, el programador puede suponer razonablemente que la precisión
extendida es relativamente rápida, por lo que programas como el ejemplo de la norma euclidiana simplemente pueden usar variables intermedias de tipo long double(o
double_t). Por otro lado, la misma implementación debe mantener las expresiones anónimas con precisión extendida incluso cuando se almacenan en la memoria (por ejemplo,
cuando el compilador debe derramar registros de punto flotante), y debe almacenar los resultados de las expresiones asignadas a las variables declaradas.doublepara
convertirlos a precisión doble incluso si se hubieran mantenido en registros. Por lo tanto, doubleni el double_ttipo ni el tipo pueden compilarse para producir el código más rápido
en el hardware actual de base extendida.

Del mismo modo, el estándar C99 proporciona soluciones a algunos de los problemas ilustrados por los ejemplos en esta sección pero no a todos. Se log1pgarantiza que una
versión estándar de la función C99 funciona correctamente si la expresión 1.0 + xse asigna a una variable (de cualquier tipo) y esa variable se usa en todas partes. Sin
embargo, un programa estándar portátil y eficiente de C99 para dividir un número de precisión doble en partes altas y bajas es más difícil: ¿cómo podemos dividirnos en la
posición correcta y evitar el doble redondeo si no podemos garantizar que las doubleexpresiones se redondeen correctamente a la precisión doble? ? Una solución es usar
eldouble_tEscriba para realizar la división en precisión doble en sistemas simples / dobles y en precisión extendida en sistemas de base extendida, de modo que en cualquier
caso la aritmética se redondee correctamente. El teorema 14 dice que podemos dividir en cualquier posición de bit siempre que conozcamos la precisión de la aritmética
subyacente, y las FLT_EVAL_METHODmacros de parámetros ambientales y deben proporcionarnos esta información.

El siguiente fragmento muestra una posible implementación:

#include <math.h>

#include <float.h>

#if (FLT_EVAL_METHOD == 2)

#define PWR2 LDBL_MANT_DIG - (DBL_MANT_DIG / 2)

#elif ((FLT_EVAL_METHOD == 1) || (FLT_EVAL_METHOD == 0))

#define PWR2 DBL_MANT_DIG - (DBL_MANT_DIG / 2)

#más

#error FLT_EVAL_METHOD desconocido!

#terminara si

...

doble x, xh, xl;

double_t m;

m = escalonamiento (1.0, PWR2) + 1.0; // 2 ** PWR2 + 1

xh = (m * x) - ((m * x) - x);

xl = x - xh;

Por supuesto, para encontrar esta solución, el programador debe saber que las doubleexpresiones pueden evaluarse con precisión extendida, que el problema del doble
redondeo puede causar un mal funcionamiento del algoritmo, y que la precisión extendida puede usarse en su lugar de acuerdo con el Teorema 14. Más La solución obvia es
simplemente especificar que cada expresión se redondee correctamente a doble precisión. En sistemas de base extendida, esto simplemente requiere cambiar el modo de
precisión de redondeo, pero desafortunadamente, el estándar C99 no proporciona una forma portátil de hacerlo. (Los primeros borradores de las ediciones de punto flotante
C, el documento de trabajo que especificaba los cambios a realizar en el estándar C90 para admitir el punto flotante, recomendaron que las implementaciones en sistemas con
modos de precisión de redondeo proporcionen fegetprecyfesetprecFunciones para obtener y establecer la precisión de redondeo, análogas a las funciones fegetroundy fesetroundque
obtienen y establecen la dirección de redondeo. Esta recomendación se eliminó antes de que se hicieran los cambios a la norma C99.)

Casualmente, el enfoque del estándar C99 para admitir la portabilidad entre sistemas con diferentes capacidades aritméticas de enteros sugiere una mejor manera de admitir
diferentes arquitecturas de punto flotante. Cada implementación estándar de C99 proporciona un <stdint.h>archivo de encabezado que define los tipos de enteros que admite
la implementación, nombrados según su tamaño y eficiencia: por ejemplo, int32_tes un tipo de entero de 32 bits de ancho, int_fast16_tes el tipo de entero más rápido de la
implementación con al menos 16 bits de ancho, y intmax_tes el tipo entero más ancho soportado. Uno puede imaginar un esquema similar para los tipos de punto flotante: por
ejemplo, float53_tpodría nombrar un tipo de punto flotante con una precisión de 53 bits, pero posiblemente con un rango más ampliofloat_fast24_tpodría nombrar el tipo más
rápido de la implementación con una precisión de al menos 24 bits, y floatmax_tpodría nombrar el tipo más ancho razonablemente rápido admitido. Los tipos rápidos podrían
permitir que los compiladores en sistemas de base extendida generen el código más rápido posible solo a la restricción de que los valores de las variables nombradas no
deben cambiar como resultado del derrame de registros. Los tipos de ancho exactos harían que los compiladores en sistemas de base extendida configuren el modo de
precisión de redondeo para redondear a la precisión especificada, permitiendo un rango más amplio sujeto a la misma restricción. Finalmente,double_tpodría nombrar un tipo
con la precisión y el rango del formato doble IEEE 754, proporcionando una doble evaluación estricta. Junto con las macros de parámetros ambientales nombradas en
consecuencia, tal esquema admitiría fácilmente las cinco opciones descritas anteriormente y permitiría a los programadores indicar de manera fácil y sin ambigüedades la
semántica de punto flotante que requieren sus programas.

¿El soporte de idiomas para una mayor precisión debe ser tan complicado? En sistemas simples / dobles, cuatro de las cinco opciones enumeradas anteriormente coinciden, y
no hay necesidad de diferenciar los tipos de ancho rápido y exacto. Los sistemas de base extendida, sin embargo, plantean elecciones difíciles: no admiten ni la precisión
doble pura ni la computación de precisión extendida pura tan eficientemente como una mezcla de las dos, y diferentes programas requieren diferentes mezclas. Además, la
elección de cuándo usar la precisión extendida no debe dejarse a los escritores de compiladores, quienes a menudo son tentados por los puntos de referencia (y en ocasiones
a los analistas numéricos les dicen abiertamente) que consideren la aritmética de punto flotante como "inherentemente inexacta" y, por lo tanto, ni merecen ni son capaces.
De la predictibilidad de la aritmética entera. En cambio, la elección debe ser presentada a los programadores,

Conclusión

Las observaciones anteriores no pretenden desprestigiar los sistemas de base extendida sino exponer varias falacias, siendo la primera que todos los sistemas IEEE 754 deben
proporcionar resultados idénticos para el mismo programa. Nos hemos centrado en las diferencias entre los sistemas de base extendida y los sistemas simples / dobles, pero
hay diferencias adicionales entre los sistemas dentro de cada una de estas familias. Por ejemplo, algunos sistemas simples / dobles proporcionan una sola instrucción para
multiplicar dos números y agregar un tercero con un solo redondeo final. Esta operación, llamada fusionar multiplicar-agregar, puede hacer que el mismo programa produzca
resultados diferentes en diferentes sistemas simples / dobles y, al igual que la precisión extendida, incluso puede hacer que el mismo programa produzca resultados diferentes
en el mismo sistema dependiendo de si se usa y cuándo. (Un agregado multiplicado fusionado también puede frustrar el proceso de división del Teorema 6, aunque puede
usarse de manera no portátil para realizar multiplicaciones de precisión múltiple sin la necesidad de división). Aunque el estándar IEEE no anticipó tal no obstante, se ajusta al
funcionamiento: el producto intermedio se entrega a un "destino" más allá del control del usuario que es lo suficientemente amplio como para contenerlo exactamente, y la
suma final se redondea correctamente para que se ajuste a su destino de precisión simple o doble.

La idea de que IEEE 754 prescribe con precisión el resultado que debe entregar un programa determinado es, sin embargo, atractiva. A muchos programadores les gusta
creer que pueden entender el comportamiento de un programa y probar que funcionará correctamente sin hacer referencia al compilador que lo compila o la computadora que
lo ejecuta. En muchos sentidos, apoyar esta creencia es un objetivo que vale la pena para los diseñadores de sistemas informáticos y lenguajes de programación.
Desafortunadamente, cuando se trata de aritmética de punto flotante, el objetivo es virtualmente imposible de lograr. Los autores de los estándares IEEE lo sabían y no
intentaron lograrlo. Como resultado, a pesar de la conformidad casi universal con (la mayoría de) el estándar IEEE 754 en toda la industria de la computación, los
programadores de software portátil deben continuar haciendo frente a la aritmética de punto flotante impredecible.

Si los programadores explotan las características de IEEE 754, necesitarán lenguajes de programación que hagan predecible la aritmética de punto flotante. El estándar C99
mejora hasta cierto punto la previsibilidad a costa de requerir que los programadores escriban varias versiones de sus programas, una para cada unoFLT_EVAL_METHOD. Ya sea que
los futuros idiomas opten por permitir que los programadores escriban un solo programa con una sintaxis que exprese de forma inequívoca hasta qué punto depende de la
semántica IEEE 754, aún está por verse. Los sistemas existentes de base extendida amenazan esa perspectiva al tentarnos a asumir que el compilador y el hardware pueden
saber mejor que el programador cómo se debe realizar un cálculo en un sistema determinado. Esa suposición es la segunda falacia: la precisión requerida en un resultado
computado no depende de la máquina que lo produce, sino de las conclusiones que se extraerán de él, y del programador, el compilador y el hardware, en el mejor de los
casos. El programador puede saber cuales son esas conclusiones.
1
Ejemplos de otras representaciones son la barra flotante y el logaritmo firmado [Matula y Kornerup 1985; Swartzlander y Alexopoulos 1975].

2
Este término fue introducido por Forsythe y Moler [1967], y generalmente ha reemplazado el término más antiguo mantisa .

3
Esto supone la disposición habitual donde el exponente se almacena a la izquierda del significante.

4
A menos que el número z sea mayor que +1 o menor que . Los números que están fuera de rango de esta manera no se considerarán hasta nuevo aviso.

5
Sea z el número de punto flotante que se aproxima a z . Entonces dd ... d - ( z / e ) p-1 es equivalente a z '- z / ulp ( z '). Una fórmula más precisa para medir el error es z '- z / ulp ( z ). - Ed.

6
700, no 70. Desde .1 - .0292 = .0708, el error en términos de ulp (0.0292) es de 708 ulps. - Ed.

7
2 2 2 2
Aunque la expresión ( x - y ) ( x + y ) no causa una cancelación catastrófica, es ligeramente menos precisa que x -y si o . En este caso, ( x - y ) ( x + y ) tiene tres errores de redondeo, pero x -y tiene solo
2 2
dos, ya que el error de redondeo cometido al calcular el menor de x yy no afecta la resta final.
8
También se conoce comúnmente como correctamente redondeado . - Ed.

9
Cuando n = 845, x n = 9.45, x n + 0.555 = 10.0, y 10.0 - 0.555 = 9.45. Por lo tanto, x n = x 845 para n > 845.

10
Note que en binario, q no puede ser igual . - Ed.

11
Dejado como un ejercicio para el lector: extienda la prueba a otras bases que no sean 2. - Ed.

12 13
Esto parece haber sido publicado por primera vez por Goldberg [1967], aunque Knuth ([1981], página 211) atribuye esta idea a Konrad Zuse. Según Kahan, la precisión extendida tiene 64 bits de significación porque esa era la precisión más amplia a través de la cual se
14
podía realizar la propagación en el Intel 8087 sin aumentar el tiempo del ciclo [Kahan 1988]. Kahan y LeBlanc [1985] presentan algunos argumentos en contra de incluir el producto interno como una de las operaciones básicas.

15 16
Kirchner escribe: Es posible calcular productos internos con un máximo de 1 ulp en hardware en un producto parcial por ciclo de reloj. El hardware adicionalmente necesario se compara con la matriz multiplicadora necesaria de todos modos para esa velocidad. CORDIC es
un acrónimo de Ordenador Digital de Rotación de Coordenadas y es un método para calcular funciones trascendentales que utiliza principalmente cambios y adiciones (es decir, muy pocas multiplicaciones y divisiones) [Walther 1971]. Es el método que se necesita adicionalmente
17
en comparación con la matriz multiplicadora necesaria para esa velocidad. d utilizado tanto en Intel 8087 como en Motorola 68881. Punto fino: aunque la aritmética IEEE predeterminada es redondear los números desbordados a , es posible
cambiar la predeterminada (consulte Modos de redondeo

18
Se llaman subnormales en 854, denormales en 754.

19 20
Esta es la causa de uno de los aspectos más problemáticos de la norma. Los programas que con frecuencia se desbordan a menudo se ejecutan notablemente más lento en el hardware que utiliza las trampas de software. No se genera una excepción no válida a
menos que haya un NaN "atrapado" involucrado en la operación. Ver la sección 6.2 de IEEE Std 754-1985. - Ed.

21
puede ser mayor que si tanto x como y son negativos. - Ed.

22
-n
Puede estar dentro del rango porque si x <1, n <0 y x es solo un poquito más pequeño que el umbral de desbordamiento , entonces , y por lo tanto no puede desbordarse, ya que en todas las precisiones IEEE,
- e min < e max .

23
Probablemente esto se deba a que a los diseñadores les gustan los conjuntos de instrucciones "ortogonales", donde las precisiones de una instrucción de punto flotante son independientes de la operación real.
Hacer un caso especial de multiplicación destruye esta ortogonalidad.

24
Esto supone la convención común que es una constante de precisión simple, mientras que es una constante de precisión doble. 3.03.0D0

25
0 ( )
La conclusión de que 0 = 1 depende de la restricción de que f no es constante. Si se elimina esta restricción, al dejar que f sea la función idénticamente 0, da 0 como posible valor para lim x 0 f ( x ) g x , y
0
entonces 0 debería definirse como un NaN.

26 27
En el caso de 0
0
, se pueden hacer argumentos de plausibilidad, pero el argumento convincente se encuentra en "Matemáticas concretas" de Graham, Knuth y Patashnik, y argumenta que 0 0 = 1 para que el teorema del binomio funcione. - Ed. A menos que el
modo de redondeo sea redondo hacia - , en cuyo caso x - x = -0.

28
bibliotecas matemáticas El VMS en VAX utilizan una forma débil de sustitución de procedimiento en línea, ya que usan el salto barata de llamada de subrutina en lugar de la más lenta y las instrucciones. CALLSCALLG

29
La dificultad con la presustitución es que requiere implementación directa de hardware o trampas de punto flotante continuadas si se implementa en el software. - Ed.

30
En esta prueba informal, suponga que = 2 para que la multiplicación por 4 sea exacta y no requiera una i.

31
Esta es la suma si sumar w no genera carry. Se necesita un argumento adicional para el caso especial en el que añadir w genera genera. - Ed.

32 El
( ) . Ed.
redondeo da k x + w k - r k solo si k x + w k mantiene la forma de k x -

Sun Microsystems, Inc. Biblioteca | Contenidos | Anterior | Siguiente | Índice


Información de copyright . Todos los derechos reservados.
Realimentación

También podría gustarte