首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在两个版本的ubuntu上用g++编译sin函数的浮点数差异

在两个版本的ubuntu上用g++编译sin函数的浮点数差异
EN

Stack Overflow用户
提问于 2022-10-14 12:52:47
回答 2查看 81关注 0票数 1

我已经测试了我的代码开发在一个ubuntu18.04仿生对接图像在一个ubuntu20.04焦点对接图像。我发现我的单元测试有一个问题,我已经将根本原因缩小为一个简单的main.cpp。

代码语言:javascript
复制
#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++版本:

代码语言:javascript
复制
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++编译器保持与我的编译器相同的结果?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-10-14 21:30:45

那么,研究一个稍微修改过的测试程序版本:

代码语言:javascript
复制
#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)使用cmathstd::sin代替math.h;2)还打印计算出的正弦值的十六进制表示。这里使用GCC 11.2在Ubuntu 22.04。

没有优化我得到

代码语言:javascript
复制
$ g++ prec1.cpp
$ ./a.out 
0.38397243618965148926 0.37460660934448242188 0x1.7f98ep-2

这就是你在Ubuntu20.04上得到的结果。但是,在启用优化之后:

代码语言:javascript
复制
$ g++ -O2 prec1.cpp
$ ./a.out 
0.38397243618965148926 0.37460657954216003418 0x1.7f98dep-2

这就是你在Ubuntu 18.04上得到的。

那么,它为什么会根据优化级别产生不同的结果呢?研究生成的汇编程序代码提供了一个线索:

代码语言:javascript
复制
$ 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中)。现在,对于优化版本:

代码语言:javascript
复制
$ g++ -O2 prec1.cpp -S
$ grep sin prec1.s
$ 

空的!那是什么意思?这意味着,该值不是在运行时调用sinf,而是在编译时计算(GCC使用MPFR库表示常量折叠浮点表达式)。

结果是不同的,因为根据优化级别的不同,一种是使用两种不同的正弦函数实现。

现在,最后,让我们看看我的修改测试程序打印的十六进制值。您可以看到未优化的值以e0结尾(由于它是小数值而没有打印0),而对于优化值则以de结尾。如果我的脑力十六进制算法是正确的,那就是2 ulp的差异,那么,你不能期望三角函数的实现会比这少一些。

票数 2
EN

Stack Overflow用户

发布于 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函数并将值本身折为常数。在这种情况下,结果可能有不同的四舍五入或准确性。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74069545

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档