Aritmética de coma flotante

Bienvenido a otra entrega de Under The Hood . Esta columna tiene como objetivo dar a los desarrolladores de Java una idea de la belleza oculta debajo de sus programas Java en ejecución. La columna de este mes continúa la discusión, comenzada el mes pasado, del conjunto de instrucciones de código de bytes de la máquina virtual Java (JVM). Este artículo echa un vistazo a la aritmética de punto flotante en la JVM y cubre los códigos de bytes que realizan operaciones aritméticas de punto flotante. Los artículos posteriores discutirán otros miembros de la familia de códigos de bytes.

Los principales puntos flotantes

El soporte de punto flotante de la JVM se adhiere al estándar de punto flotante IEEE-754 1985. Este estándar define el formato de los números de coma flotante de 32 y 64 bits y define las operaciones sobre esos números. En la JVM, la aritmética de punto flotante se realiza en flotantes de 32 bits y dobles de 64 bits. Para cada bytecode que realiza operaciones aritméticas en flotantes, existe un bytecode correspondiente que realiza la misma operación en dobles.

Un número de punto flotante tiene cuatro partes: un signo, una mantisa, una base y un exponente. El signo es 1 o -1. La mantisa, siempre un número positivo, contiene los dígitos significativos del número de coma flotante. El exponente indica la potencia positiva o negativa de la base por la que se deben multiplicar la mantisa y el signo. Los cuatro componentes se combinan de la siguiente manera para obtener el valor de punto flotante:

signo * mantisa * exponente de base

Los números de coma flotante tienen múltiples representaciones, porque siempre se puede multiplicar la mantisa de cualquier número de coma flotante por alguna potencia de la base y cambiar el exponente para obtener el número original. Por ejemplo, el número -5 se puede representar igualmente mediante cualquiera de las siguientes formas en la base 10:

Formas de -5
Firmar Mantisa Exponente de la raíz
-1 50 10 -1
-1 5 10 0
-1 0,5 10 1
-1 0,05 10 2

Para cada número de coma flotante hay una representación que se dice que está normalizada. Un número de punto flotante se normaliza si su mantisa está dentro del rango definido por la siguiente relación:

1 / raíz <= mantisa <

Un número de punto flotante de base 10 normalizado tiene su punto decimal justo a la izquierda del primer dígito distinto de cero en la mantisa. La representación de punto flotante normalizado de -5 es -1 * 0.5 * 10 1. En otras palabras, la mantisa de un número de punto flotante normalizado no tiene dígitos distintos de cero a la izquierda del punto decimal y un dígito distinto de cero solo para a la derecha del punto decimal. Se dice que cualquier número de punto flotante que no encaja en esta categoría está desnormalizado . Tenga en cuenta que el número cero no tiene una representación normalizada, porque no tiene un dígito distinto de cero para colocarlo justo a la derecha del punto decimal. "¿Por qué estar normalizado?" es una exclamación común entre ceros.

Los números de coma flotante en la JVM usan una base de dos. Los números de coma flotante en la JVM, por lo tanto, tienen la siguiente forma:

signo * mantisa * 2 exponente

La mantisa de un número de punto flotante en la JVM se expresa como un número binario. Una mantisa normalizada tiene su punto binario (el equivalente en base dos de un punto decimal) justo a la izquierda del dígito distinto de cero más significativo. Debido a que el sistema numérico binario tiene solo dos dígitos, cero y uno, el dígito más significativo de una mantisa normalizada es siempre uno.

El bit más significativo de un flotante o doble es su bit de signo. La mantisa ocupa los 23 bits menos significativos de un flotante y los 52 bits menos significativos de un doble. El exponente, 8 bits en un flotante y 11 bits en un doble, se encuentra entre el signo y la mantisa. El formato de un flotador se muestra a continuación. El bit de signo se muestra como una "s", los bits de exponente se muestran como "e" y los bits de mantisa se muestran como "m":

Diseño de bits del flotador Java
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm

Un bit de signo de cero indica un número positivo y un bit de signo de uno indica un número negativo. La mantisa siempre se interpreta como un número positivo de base dos. No es un número en complemento a dos. Si el bit de signo es uno, el valor de punto flotante es negativo, pero la mantisa aún se interpreta como un número positivo que debe multiplicarse por -1.

El campo de exponente se interpreta de tres formas. Un exponente de todos unos indica que el número de coma flotante tiene uno de los valores especiales de más o menos infinito, o "no es un número" (NaN). NaN es el resultado de ciertas operaciones, como la división de cero por cero. Un exponente de todos ceros indica un número de coma flotante desnormalizado. Cualquier otro exponente indica un número de coma flotante normalizado.

La mantisa contiene un poco más de precisión más allá de los que aparecen en los bits de mantisa. La mantisa de un flotador, que ocupa solo 23 bits, tiene 24 bits de precisión. La mantisa de un doble, que ocupa 52 bits, tiene 53 bits de precisión. El bit de mantisa más significativo es predecible y, por lo tanto, no se incluye, porque el exponente de los números de punto flotante en la JVM indica si el número está normalizado o no. Si el exponente es todo ceros, el número de punto flotante se desnormaliza y se sabe que el bit más significativo de la mantisa es un cero. De lo contrario, el número de coma flotante se normaliza y se sabe que el bit más significativo de la mantisa es uno.

La JVM no arroja excepciones como resultado de ninguna operación de punto flotante. Los valores especiales, como infinito positivo y negativo o NaN, se devuelven como resultado de operaciones sospechosas como la división por cero. Un exponente de todos unos indica un valor especial de coma flotante. Un exponente de todos los unos con una mantisa cuyos bits son todos cero indica un infinito. El signo del infinito se indica mediante el bit de signo. Un exponente de todos unos con cualquier otra mantisa se interpreta como "no un número" (NaN). La JVM siempre produce la misma mantisa para NaN, que es todo ceros excepto el bit de mantisa más significativo que aparece en el número. Estos valores se muestran para un flotador a continuación:

Valores flotantes especiales
Valor Bits flotantes (mantisa de exponente de signo)
+ Infinito 0 11111111 00000000000000000000000
-Infinito 1 11111111 00000000000000000000000
Yaya 1 11111111 10000000000000000000000

Los exponentes que no son todos unos ni todos ceros indican la potencia de dos por la que se multiplica la mantisa normalizada. La potencia de dos se puede determinar interpretando los bits del exponente como un número positivo y luego restando un sesgo del número positivo. Para un flotante, el sesgo es 126. Para un doble, el sesgo es 1023. Por ejemplo, un campo exponente en un flotante de 00000001 produce una potencia de dos al restar el sesgo (126) del campo exponente interpretado como un entero positivo (1). La potencia de dos, por tanto, es 1 - 126, que es -125. Esta es la potencia más pequeña posible de dos para un flotador. En el otro extremo, un campo exponente de 11111110 produce una potencia de dos de (254 - 126) o 128. El número 128 es la potencia más grande de dos disponibles para un flotador. En la siguiente tabla se muestran varios ejemplos de flotadores normalizados:

Valores flotantes normalizados
Valor Bits flotantes (mantisa de exponente de signo) Exponente imparcial
Flotador positivo (finito) más grande 0 11111110 11111111111111111111111 128
Flotador negativo (finito) más grande 1 11111110 11111111111111111111111 128
Flotador normalizado más pequeño 1 00000001 00000000000000000000000 -125
Pi 0 10000000 10010010000111111011011 2

Un exponente de todos ceros indica que la mantisa está desnormalizada, lo que significa que el bit inicial no declarado es un cero en lugar de uno. La potencia de dos en este caso es la misma que la potencia más baja de dos disponible para una mantisa normalizada. Para el flotador, esto es -125. Esto significa que las mantisas normalizadas multiplicadas por dos elevadas a la potencia de -125 tienen un campo exponente de 00000001, mientras que las mantisas desnormalizadas multiplicadas por dos elevadas a la potencia de -125 tienen un campo exponente de 00000000. La tolerancia para números desnormalizados en la parte inferior final de la gama de exponentes admite subdesbordamiento gradual. Si en cambio se usara el exponente más bajo para representar un número normalizado, se produciría un subdesbordamiento a cero para números más grandes. En otras palabras, dejar el exponente más bajo para números desnormalizados permite representar números más pequeños.Los números desnormalizados más pequeños tienen menos bits de precisión que los números normalizados, pero esto es preferible al subdesbordamiento a cero tan pronto como el exponente alcanza su valor mínimo normalizado.

Valores flotantes desnormalizados
Valor Bits flotantes (mantisa de exponente de signo)
Flotador positivo más pequeño (distinto de cero) 0 00000000 00000000000000000000001
Flotador negativo (distinto de cero) más pequeño 1 00000000 00000000000000000000001
El flotador desnormalizado más grande 1 00000000 11111111111111111111111
Cero positivo 0 00000000 00000000000000000000000
Cero negativo 1 00000000 00000000000000000000000

Flotador expuesto

Un flotante de Java revela su naturaleza interna El subprograma a continuación le permite jugar con el formato de punto flotante. El valor de un flotante se muestra en varios formatos. El formato de notación científica de base dos muestra la mantisa y el exponente en base diez. Antes de mostrarse, la mantisa real se multiplica por 2 24, lo que produce un número entero, y el exponente insesgado se reduce en 24. Tanto la mantisa integral como el exponente se convierten fácilmente en base diez y se muestran.