ICC编译下相同汇编输出不同?void*p=f为何影响浮点计算结果
示例代码(t0.c)
#include <stdio.h> float f(float a, float b, float c) __attribute__((noinline)); float f(float a, float b, float c) { return a * c + b * c; } int main(void) { void* p = V; printf("%a\n", f(4476.0f, 20439.0f, 4915.0f)); return 0; }
编译与执行(icc 2021.1.2 Linux x86-64 环境)
# 编译参数1:-DV=f $ icc t0.c -fp-model=fast -O3 -DV=f # 运行输出 0x1.d32322p+26 # 编译参数2:-DV=0 $ icc t0.c -fp-model=fast -O3 -DV=0 # 运行输出 0x1.d32324p+26
问题解答
你观测到的「汇编完全一致」仅指f函数的生成代码,两个编译选项下main函数的实现存在本质差异:
- 当使用
-DV=0参数时,编译器没有检测到任何地方取f的地址,结合-O3的过程间优化,编译器可以判定整个程序中只有main会直接调用f,没有其他通过函数指针调用f的场景。此时-fp-model=fast允许编译器对浮点运算做激进优化:对f的常量参数调用直接做常量折叠,折叠时使用了更高精度的计算逻辑(比如将a*c + b*c优化为(a + b) * c减少一次舍入,或者直接使用双精度/FMA指令完成计算),预计算的结果直接作为常量传给printf,整个过程根本不会执行f函数的代码。 - 当使用
-DV=f参数时,你显式取了f的地址,编译器必须保证f可以被外部通过函数指针调用,所以f的实现必须严格符合你编写的逻辑:先计算a*c并舍入到float,再计算b*c并舍入到float,最后将两个结果相加再舍入到float。这时候对f的常量调用要么直接生成调用f的代码,要么常量折叠时严格按照f的实现逻辑求值,得到的结果就是精度稍低的0x1.d32322p+26。
void* p = f;这行代码的作用是让编译器检测到f的地址发生了逃逸,无法再假设f的调用场景只有当前翻译单元的直接调用,因此必须保证f的所有调用行为(直接调用、指针调用)的结果完全一致,自然无法再对f的调用做激进的浮点精度优化的常量折叠。
内容的提问来源于stack exchange,提问作者pmor
相关产品推荐
相关产品推荐

