我已经测试了我的代码开发在一个ubuntu18.04仿生对接图像在一个ubuntu20.04焦点对接图像。我发现我的单元测试有一个问题,我已经将根本原因缩小为一个简单的main.cpp。
#include <iostream>
#include <iomanip>
#include <math.h>
int main()
{
const float DEG_TO_RAD_FLOAT = float(M_PI / 180.);
float theta = 22.0f;
theta = theta * DEG_TO_RAD_FLOAT;
std::cout << std::setprecision(20) << theta << ' ' << sin(theta) << std::endl;
return 0;
}在仿生坞映像中,我使用以下命令升级了我的g++版本:
sudo apt-get install -y software-properties-common
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt install -y gcc-9 g++-9
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 --slave /usr/bin/g++ g++ /usr/bin/g++-9 --slave /usr/bin/gcov gcov /usr/bin/gcov-9
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 --slave /usr/bin/gcov gcov /usr/bin/gcov-7我的g++版本是一样的: 9.4.0。
在ubuntu18.04上,程序输出: 0.38397243618965148926 0.37460657954216003418
在ubuntu20.04上,程序输出: 0.38397243618965148926 0.37460660934448242188
正如你所看到的,差别是在罪恶(θ)上,在小数点7。我能想到的唯一不同是libc的版本,它在ubuntu18.04上是2.27,在ubuntu20.04上是2.31。
我尝试过几个g++选项,-mfpmath=sse, -fPIC,-ffloat-store, -msse, -msse2,但没有效果。
真正的问题是,在用/fp:precise编译的Windows版本中,我得到的结果与Ubuntu18.04: 0.38397243618965148926 0.37460657954216003418相同
有没有办法强迫g++编译器保持与我的编译器相同的结果?
发布于 2022-10-14 21:30:45
那么,研究一个稍微修改过的测试程序版本:
#include <iostream>
#include <iomanip>
#include <cmath>
int main()
{
const float DEG_TO_RAD_FLOAT = float(M_PI / 180.);
float theta = 22.0f;
theta = theta * DEG_TO_RAD_FLOAT;
std::cout << std::setprecision(20) << theta << ' ' << std::sin(theta)
<< ' ' << std::hexfloat << std::sin(theta) << std::endl;
return 0;
}改变是: 1)使用cmath和std::sin代替math.h;2)还打印计算出的正弦值的十六进制表示。这里使用GCC 11.2在Ubuntu 22.04。
没有优化我得到
$ g++ prec1.cpp
$ ./a.out
0.38397243618965148926 0.37460660934448242188 0x1.7f98ep-2这就是你在Ubuntu20.04上得到的结果。但是,在启用优化之后:
$ g++ -O2 prec1.cpp
$ ./a.out
0.38397243618965148926 0.37460657954216003418 0x1.7f98dep-2这就是你在Ubuntu 18.04上得到的。
那么,它为什么会根据优化级别产生不同的结果呢?研究生成的汇编程序代码提供了一个线索:
$ g++ prec1.cpp -S
$ grep sin prec1.s
.section .text._ZSt3sinf,"axG",@progbits,_ZSt3sinf,comdat
.weak _ZSt3sinf
.type _ZSt3sinf, @function
_ZSt3sinf:
call sinf@PLT
.size _ZSt3sinf, .-_ZSt3sinf
call _ZSt3sinf
call _ZSt3sinf那这是什么意思?它称之为sinf (它生活在glibc的数学库libm中)。现在,对于优化版本:
$ g++ -O2 prec1.cpp -S
$ grep sin prec1.s
$ 空的!那是什么意思?这意味着,该值不是在运行时调用sinf,而是在编译时计算(GCC使用MPFR库表示常量折叠浮点表达式)。
结果是不同的,因为根据优化级别的不同,一种是使用两种不同的正弦函数实现。
现在,最后,让我们看看我的修改测试程序打印的十六进制值。您可以看到未优化的值以e0结尾(由于它是小数值而没有打印0),而对于优化值则以de结尾。如果我的脑力十六进制算法是正确的,那就是2 ulp的差异,那么,你不能期望三角函数的实现会比这少一些。
发布于 2022-10-14 14:04:37
不管是否保证对数学函数的调用的确切结果与版本变化保持一致,您也依赖于未指定的行为。
具体来说,您将<math.h>包含在C++程序中。这将使来自C标准库的sin在全局命名空间范围内可用,但未指定它是否会使来自C++标准库的sin重载在全局命名空间范围内可用。
C的sin函数在double上运行,而C++为float添加了重载。因此,您是在调用在double上运行的重载,还是调用在float上运行的重载,这是未指定的。根据这一点,您将得到不同的四舍五入的结果。
为了保证对float重载的调用,请将<cmath>替换为<cmath>,并调用std::sin而不是sin。
另外,取决于优化标志,GCC可能实际上不会调用sin函数并将值本身折为常数。在这种情况下,结果可能有不同的四舍五入或准确性。
https://stackoverflow.com/questions/74069545
复制相似问题