use*_*685 5 c++ floating-point precision visual-c++ visual-studio-2019
我正在使用 MSVC 2019 v16.11.12。
当我尝试使用/fp:fast而不是/fp:precise编译代码时,我的测试开始失败。
最简单的情况是:
BOOST_AUTO_TEST_CASE(test_division) {
float v = 3000.f;
BOOST_TEST((v / 1000.f) == 3.f);
}
Run Code Online (Sandbox Code Playgroud)
失败结果:
错误:在“test_division”中:检查 (v / 1000.f) == 3.f 失败 [3.00000024 != 3]
我知道/fp:fast在某些情况下可能具有较差的浮点精度;但是,在这里,似乎太过分了......
为什么不能准确地将 3000 除以 1000?
我知道 0.1 + 0.2 不是 0.3,但这里所有数字都是可表示的,并且除法使用fp:precise返回正好 3 。
我是否有可能翻转了其他一些标志,从而进一步降低了浮点精度?
gcc 的 -ffast-math 选项(可能还有 msvc 的 /fp:fast 选项)启用的优化之一是将“除以常量”转换为“乘以倒数”,因为浮点除法非常慢 - 在有些机器比乘法器贵 10 倍以上,因为乘法器通常采用流水线方式,而除法器则不太常见。
这样,/ 1000.f就会变成* .001一定精度的 a,而 0.001 无法用浮点数精确表示,因此会出现一些不精确的情况。
更准确地说,最接近 0.001 的 32 位 FP 值是 0x1.0624dep-10,而最接近的 64 位 FP 是 0x1.0624dd2f1a9fcp-10。如果该 32 位值乘以 3000,您将得到 0x1.80000132p+1 或大约 3.0000001425。如果将其四舍五入到 32 位,您将得到 0x1.800002p+1 或大约 3.0000002384。有趣的是,如果您使用 64 位值并乘以 3000,您将得到 0x1.800000000000024p+1,当四舍五入到 64 位时,它就是精确的 0x1.8p+1 值。