valor resueltos que punto numericos notación normalizada metodos mantisa informatica flotante ejercicios ejemplos coma c++ c performance optimization compiler-optimization

c++ - resueltos - que es coma flotante en informatica



¿Por qué un compilador no optimiza el punto flotante*2 en un incremento de exponente? (8)

Por ejemplo, 2 * f, simplemente podría incrementar el exponente de f por 1, guardando algunos ciclos.

Esto simplemente no es verdad.

Primero tiene demasiados casos de esquina como cero, infinito, Nan y denormal. Entonces tienes el problema de rendimiento.

El malentendido es que incrementar el exponente no es más rápido que hacer una multiplicación.

Si observa las instrucciones de hardware, no hay una forma directa de incrementar el exponente. Entonces, lo que tienes que hacer es:

  1. Bitwise convertir a entero.
  2. Incrementa el exponente.
  3. Bitwise convierte de nuevo a punto flotante.

En general, existe una latencia de medio a grande para mover datos entre el entero y las unidades de ejecución de coma flotante. Entonces, al final, esta "optimización" se vuelve mucho peor que una simple multiplicación de coma flotante.

Entonces, la razón por la cual el compilador no hace esta "optimización" es porque no es más rápido.

A menudo he notado que gcc convierte multiplicaciones en turnos en el ejecutable. Algo similar podría suceder al multiplicar un int y un float . Por ejemplo, 2 * f , simplemente podría incrementar el exponente de f por 1, guardando algunos ciclos. Haga los compiladores, tal vez si uno los solicita hacerlo (por ejemplo, a través de -ffast-math ), en general, ¿hacerlo?

¿Los compiladores generalmente son lo suficientemente inteligentes como para hacer esto, o debo hacerlo yo mismo usando la familia de funciones scalb*() o ldexp()/frexp() ?


En las CPUs modernas, la multiplicación generalmente tiene un rendimiento por ciclo y baja latencia. Si el valor ya está en un registro de coma flotante, no hay manera de que lo superes haciendo malabares para hacer una aritmética entera en la representación. Si está en la memoria para empezar, y si está asumiendo que ni el valor actual ni el resultado correcto serían cero, denormal, nan o infinito, entonces podría ser más rápido realizar algo como

addl $0x100000, 4(%eax) # x86 asm example

multiplicar por dos; La única vez que pude ver que esto era beneficioso es si está operando en una gran variedad de datos de punto flotante que están separados de cero e infinito, y escalar con una potencia de dos es la única operación que realizará (por lo tanto, no tiene ningún motivo para cargar los datos en registros de coma flotante).


En realidad, esto es lo que sucede en el hardware.

El 2 también se pasa a la FPU como un número de coma flotante, con una mantisa de 1.0 y un exponente de 2 ^ 1. Para la multiplicación, los exponentes se agregan y las mantisas se multiplican.

Dado que hay hardware dedicado para manejar el caso complejo (multiplicándose con valores que no son potencias de dos), y el caso especial no se maneja peor de lo que sería usando hardware dedicado, no tiene sentido tener circuitos e instrucciones adicionales. .


Los formatos comunes de punto flotante, particularmente IEEE 754, no almacenan el exponente como un entero simple, y tratarlo como un entero no producirá resultados correctos.

En flotante de 32 bits o doble de 64 bits, el campo de exponente es de 8 u 11 bits, respectivamente. Los códigos de exponente 1 a 254 (en float) o 1 a 2046 (en doble) actúan como enteros: si agrega uno a uno de estos valores y el resultado es uno de estos valores, el valor representado se duplica. Sin embargo, agregar uno falla en estas situaciones:

  • El valor inicial es 0 o subnormal. En este caso, el campo de exponente comienza en cero, y al agregar uno agrega 2 -126 (en float) o 2 -1022 (en doble) al número; no duplica el número.
  • El valor inicial excede 2 127 (en float) o 2 1023 (en doble). En este caso, el campo de exponente comienza en 254 o 2046, y al agregar uno cambia el número a un NaN; no duplica el número.
  • El valor inicial es infinito o NaN. En este caso, el campo de exponente comienza en 255 o 2047, y al agregar uno cambia a cero (y es probable que se desborde en el bit de signo). El resultado es cero o subnormal, pero debe ser infinito o NaN, respectivamente.

(Lo anterior es para signos positivos. La situación es simétrica con signos negativos).

Como otros han notado, algunos procesadores no tienen facilidades para manipular los bits de los valores de punto flotante rápidamente. Incluso en los que sí lo hacen, el campo de exponente no está aislado de los otros bits, por lo que normalmente no puede agregar uno sin desbordamiento en el bit de signo en el último caso anterior.

Aunque algunas aplicaciones pueden tolerar accesos directos como descuidar subnormales o NaN o incluso infinitos, es raro que las aplicaciones puedan ignorar cero. Como agregar uno al exponente no puede manejar el cero correctamente, no se puede usar.


No se trata de compiladores o escritores de compiladores que no sean inteligentes. Es más como obedecer los estándares y producir todos los "efectos secundarios" necesarios, como Infs, Nans y denormals.

También puede tratarse de no producir otros efectos secundarios que no se requieren, como leer la memoria. Pero sí reconozco que puede ser más rápido en algunas circunstancias.


Puede ser útil para los compiladores de sistemas integrados tener pseudo-op escala-por-potencia especial de dos que pueda ser traducida por el generador de código de la manera que sea óptima para la máquina en cuestión, ya que en algunos procesadores integrados que se centran en el exponente puede ser un orden de magnitud más rápido que hacer una multiplicación completa de potencia de dos, pero en los micros incrustados donde la multiplicación es más lenta, un compilador probablemente podría lograr un mayor aumento de rendimiento haciendo que la rutina de multiplicación flotante verifique sus argumentos en tiempo de ejecución para omitir partes de la mantisa que son cero.


Si crees que multiplicar por dos significa aumentar el exponente por 1, piénsalo de nuevo. Aquí están los casos posibles para la aritmética de coma flotante IEEE 754:

Caso 1: Infinity y NaN permanecen sin cambios.

Caso 2: los números de coma flotante con el mayor exponente posible se cambian a Infinito al aumentar el exponente y establecer la mantisa a excepción del bit de signo a cero.

Caso 3: los números de coma flotante normalizados con un exponente menor que el exponente máximo posible aumentan su exponente en uno. Yippee !!!

Caso 4: los números de coma flotante desnormalizados con el conjunto de bits de mantisa más alto aumentan su exponente en uno, convirtiéndolos en números normalizados.

Caso 5: Los números de punto flotante desnormalizados con el bit de mantisa más alto despejado, incluidos +0 y -0, tienen su mantisa desplazada hacia la izquierda en una posición de bit, sin modificar el exponente.

Dudo mucho que un compilador que produce código entero manejando todos estos casos correctamente sea tan rápido como el punto flotante integrado en el procesador. Y solo es adecuado para la multiplicación por 2.0. Para la multiplicación por 4.0 o 0.5, se aplica un nuevo conjunto de reglas. Y para el caso de la multiplicación por 2.0, puede intentar reemplazar x * 2.0 con x + x, y muchos compiladores lo hacen. Eso es lo que hacen, porque un procesador podría, por ejemplo, hacer una suma y una multiplicación al mismo tiempo, pero no una de cada tipo. Por lo tanto, a veces preferiría x * 2.0 y, a veces, x + x, dependiendo de lo que otras operaciones necesiten hacer al mismo tiempo.