我写了一个小程序 计算 </跨度> 3坐标向量的欧几里德范数。这里是:
template&lt; typename T,std :: size_t N&gt;auto norm(const ,减法,乘法和平方根。如果我 信任 </跨度> 我上面引用的文章,考虑到它在单个线程中运行并且我不改变舍入模式或其他任何浮点
浮点数的精度存在差异,具体取决于存储位置。如果编译器将一个变量保存在寄存器中,则它作为存储在内存中的变量具有更高的精度。您可以尝试强制将变量存储在内存中,例如:
int main() { std::array<double, 3u> arr = { 4.0, -2.0, 6.0 }; volatile double v1 = norm(arr); volatile double v2 = norm(arr); std::cout << v1 - v2 << '\n'; }
这样可以得到0的预期结果。
正如@MatthiasB指出的那样,这似乎是gcc暂时将80位浮点值存储到64位寄存器/存储器位置的问题。请考虑以下简化程序仍然可以重现该问题:
#include <cmath> #include <iostream> double norm() { double res = 4.0 * 4.0 + (-2.0 * -2.0) + (6.0 * 6.0); return std::sqrt(res); } int main() { std::cout << norm() - norm() << '\n'; return 0; }
必不可少的部分的汇编代码 norm() - norm() 看起来像这样(使用32位mingw 4.8.0编译器)
norm() - norm()
... call __Z4normv ; call norm() fstpl -16(%ebp) ; store result (80 bit) in temporary (64 bit!) call __Z4normv ; call norm() again fsubrl -16(%ebp) ; subtract result (80 bit) from temporary (64 bit!) ...
从本质上讲,我会认为这是一个gcc bug,但它似乎是一个 复杂的话题 ...