在C语言中,相同float操作数下+=与+结果不同是否合法?
关于C语言浮点运算中
+=与+行为不一致的疑问 我的初始认知
- float的加法和乘法运算具有确定性;
- 当操作数A无副作用时,
A += B的执行逻辑应当与A = A + B完全一致。
问题复现
但以下程序中的断言却意外失败:
#include <stdio.h> #include <assert.h> void trial(long long i, float *src, float *target, float g) { float a = target[i]; float x = g * src[i]; printf("%g %g %g\n", a, x, a + x); target[i] += g * src[i]; assert(target[i] == a + x); } int main() { float target[2] = { 0.0, -4.52271e-06 }; float src[2] = { -0.000926437, -0.00102722 }; trial(1, src, target, 0.01235); return 0; }
在支持足够SSE指令的x86平台上,断言必然失败:
- 使用
clang -O2 -march=native -Wall -o test test.c编译后,运行输出如下:
-4.52271e-06 -1.26862e-05 -1.72089e-05 test: test.c:9: void trial(long long, float *, float *, float): Assertion `target[i] == a + x' failed. Aborted (core dumped)
- GCC编译下断言同样失败,甚至Clang用
-O0编译时也会出现断言失败的情况。
原因分析
查看汇编代码后发现:编译器为+=操作生成了**融合乘加(fused multiply-add)**指令,但单独的+运算却没有使用该指令。融合乘加指令不会先将乘法结果舍入到最近的float值,而是直接将乘法的中间结果与加数进行运算,最终只做一次舍入,这就导致了结果差异。
核心疑问
这种行为是否符合C标准?我了解到最新的C标准草案在Annex F中规定,支持IEC标准浮点运算的实现必须按照该标准实现float的加法和乘法,且该标准是确定性的。但我并未找到C标准中允许省略舍入操作的相关条款。
内容的提问来源于stack exchange,提问作者Jason Orendorff
相关产品推荐
相关产品推荐

