gcc标志-funsafe-math-optimizations
(-ffast-math
的一部分)打开FTZ
和DAZ
(flush-to-zero和denormals-are-zero)。但是,打开优化会禁用此行为。
#include <stdio.h>
#include <pmmintrin.h>
int main(int argc, char** argv)
{
//_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
float normal_f = 1.18e-38f;
double normal_d = 2.23e-308;
normal_f *= 0.1f;
if (normal_f != 0.0f)
printf("FTZ/DAZ disabled for floats (%e)n", (double) normal_f);
normal_d *= 0.1;
if (normal_d != 0.0)
printf("FTZ/DAZ disabled for doubles (%e)n", normal_d);
return 0;
}
当用gcc foo.c -ffast-math
编译时,FTZ和DAZ都是启用的(即,没有输出到stdout)。但是,如果包括任何优化(例如,-O1
,-O3
,-Ofast
),则FTZ和DAZ被禁用:
$ ./a.out
FTZ/DAZ disabled for floats (1.180000e-39)
FTZ/DAZ disabled for doubles (2.230000e-309)
更奇怪的是,当我用_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON)
显式启用FTZ和DAZ时,我看到了同样的行为。优化会禁用它。此命令仅在编译时不使用- fast-math时更改任何内容。
我的问题:我如何在使用优化时实现FTZ/DAZ ?另外,是否有其他快速数学行为被优化禁用?
在gcc 10.2和6.3
看起来这个行为是我的单元测试的产物。编译器本身不服从FTZ/DAZ,只有生成的代码才服从。因此,在类似示例的情况下,编译器执行计算,并且知道绕过条件语句并直接移动到printfs是安全的。
将其分解为2个编译单元,问题就消失了:
bar.c
#include <stdio.h>
void check_normalf(float f)
{
if (f != 0.0f) {
printf("FTZ/DAZ disabled for floats (%e)n", (double) f);
}
}
void check_normal(float d)
{
if (d != 0.0) {
printf("FTZ/DAZ disabled for doubles (%e)n", d);
}
}
foo.c
#include <pmmintrin.h>
void check_normalf(float f);
void check_normal(float d);
int main(int argc, char** argv)
{
//_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
float normal_f = 1.18e-38f;
double normal_d = 2.23e-308;
check_normalf(normal_f * 0.1f);
check_normal(normal_d * 0.1);
return 0;
}
当检查函数是同一文件的一部分时,问题仍然存在,但这仍然是编译器优化。