Fusionado multiplicar agregar y modos de redondeo predeterminados

Con GCC 5.3 el siguiente código compield con -O3 -fma

 float mul_add(float a, float b, float c) { return a*b + c; } 

produce el siguiente ensamblaje

 vfmadd132ss %xmm1, %xmm2, %xmm0 ret 

Noté que GCC hacía esto con -O3 ya en GCC 4.8 .

Clang 3.7 con -O3 -mfma produce

 vmulss %xmm1, %xmm0, %xmm0 vaddss %xmm2, %xmm0, %xmm0 retq 

pero Clang 3.7 con -Ofast -mfma produce el mismo código que GCC con -O3 fast .

Me sorprende que GCC lo haga con -O3 porque de esta respuesta dice

El comstackdor no puede fusionar un complemento separado y multiplicar a menos que permita un modelo de coma flotante relajado.

Esto se debe a que un FMA tiene solo un redondeo, mientras que un ADD + MUL tiene dos. Por lo tanto, el comstackdor violará el estricto comportamiento de coma flotante de IEEE al fusionarse.

Sin embargo, de este enlace dice

Independientemente del valor de FLT_EVAL_METHOD, cualquier expresión de punto flotante puede contraerse, es decir, calcularse como si todos los resultados intermedios tuvieran un scope y precisión infinitos.

Entonces ahora estoy confundido y preocupado.

  1. ¿Tiene justificación GCC para usar FMA con -O3 ?
  2. ¿La fusión viola el estricto comportamiento de punto flotante IEEE?
  3. Si la fusión viola el comportamiento de coma flotante de IEEE y como GCC lo devuelve, __STDC_IEC_559__ ¿no es esto una contradicción?

Como FMA se puede emular en el software , parece que debe haber dos modificadores de comstackción para FMA: uno para indicar al comstackdor que use FMA en los cálculos y otro para decirle al comstackdor que el hardware tiene FMA.


Apprently esto se puede controlar con la opción -ffp-contract . Con GCC, el valor predeterminado es -ffp-contract=fast y con Clang no lo es. Otras opciones como -ffp-contract=on y -ffp-contract=off no producen la instrucción FMA.

Por ejemplo, Clang 3.7 con -O3 -mfma -ffp-contract=fast produce vfmadd132ss .


Comprobé algunas permutaciones de #pragma STDC FP_CONTRACT establecidas en ON y OFF con -ffp-contract establecido en on , off y fast . En todos los casos, también utilicé -O3 -mfma .

Con GCC, la respuesta es simple. #pragma STDC FP_CONTRACT ON u OFF no hace diferencia. Solo -ffp-contract importa.

GCC usa fma con

  1. -ffp-contract=fast (predeterminado).

Con Clang usa fma

  1. con -ffp-contract=fast .
  2. con -ffp-contract=on (predeterminado) y #pragma STDC FP_CONTRACT ON (el valor predeterminado es OFF ).

En otras palabras, con Clang puede obtener fma con #pragma STDC FP_CONTRACT ON (ya que -ffp-contract=on es el valor predeterminado) o con -ffp-contract=fast . -ffast-math (y por -Ofast tanto -Ofast ) set -ffp-contract=fast .


Miré en MSVC e ICC.

Con MSVC usa la instrucción fma con /O2 /arch:AVX2 /fp:fast . Con MSVC /fp:precise es la predeterminada.

Con ICC, utiliza fma con -O3 -O3 -march=core-avx2 (en realidad -O1 es suficiente). Esto se debe a que, de manera predeterminada, ICC usa -fp-model fast . Pero ICC usa fma incluso con -fp-model precise . Para desactivar fma con el uso de ICC -fp-model strict o -no-fma .

Entonces, por defecto, GCC e ICC usan fma cuando fma está habilitado (con -mfma para GCC / Clang o -march=core-avx2 con ICC) pero Clang y MSVC no lo hacen.

No infringe IEEE-754, ya que IEEE-754 difiere a los idiomas en este punto:

Un estándar de lenguaje también debe definir, y requerir implementaciones para proporcionar, atributos que permiten y no permiten las optimizaciones de cambio de valores, por separado o colectivamente, para un bloque. Estas optimizaciones pueden incluir, pero no están limitadas a:

– Síntesis de una operación fusedMultiplyAdd a partir de una multiplicación y una adición.

En el estándar C, el pragma de STDC FP_CONTRACT proporciona los medios para controlar esta optimización que cambia el valor. Por lo tanto, GCC tiene licencia para realizar la fusión de forma predeterminada, siempre que le permita desactivar la optimización al desactivar STDC FP_CONTRACT OFF . No apoyar eso significa no adherirse al estándar C.

Cuando citó que se permite el agregado multiplicado fusionado, omitió la condición importante “a menos que pragma FP_CONTRACT esté desactivado”. Lo cual es una característica nueva en C (creo que se introdujo en C99) y fue hecho absolutamente necesario por PowerPC, que todos fusionaron multiply-add desde el principio – en realidad, x * y era equivalente a fma (x, y, 0) y x + y fue equivalente a fma (1.0, x, y).

FP_CONTRACT es lo que controla fusionar multiplicar / agregar, no FLT_EVAL_METHOD. Aunque si FLT_EVAL_METHOD permite una mayor precisión, la contratación siempre es legal; solo pretende que las operaciones se realizaron con muy alta precisión y luego se redondearon.

La función fma es útil si no quieres la velocidad, sino la precisión. Calculará el resultado contratado lenta pero correctamente, incluso si no está disponible en hardware. Y debe estar en línea si está disponible en hardware.