不同Linux发行版中exp()函数结果差异的技术咨询
为何部分Linux发行版中
exp()数学函数返回结果不一致? 测试通过以下C++程序完成:
#include <iostream> #include <iomanip> #include <cmath> #include <limits> int main() { double a = -9.55258983280695383e-03; std::cout << std::setprecision(std::numeric_limits< double >::max_digits10) << std::scientific ; std::cout << exp(a) << ";" << expf(a) << ";" << expl(a) << std::endl; }
编译运行命令:
$ g++ -std=c++11 exp.cxx $ ./a.out
测试覆盖了double、float和long double类型的exp()函数变体,各发行版测试结果如下:
| 发行版 | exp(a) | expf(a) | expl(a) |
|---|---|---|---|
| stretch | 9.90492891217632399e-01 | 9.90492880344390869e-01 | 9.90492891217632453e-01 |
| buster | 9.90492891217632510e-01 | 9.90492880344390869e-01 | 9.90492891217632453e-01 |
| bullseye | 9.90492891217632399e-01 | 9.90492880344390869e-01 | 9.90492891217632453e-01 |
| bookworm | 9.90492891217632399e-01 | 9.90492880344390869e-01 | 9.90492891217632453e-01 |
| xenial | 9.90492891217632399e-01 | 9.90492880344390869e-01 | 9.90492891217632453e-01 |
| focal | 9.90492891217632399e-01 | 9.90492880344390869e-01 | 9.90492891217632453e-01 |
| jammy | 9.90492891217632399e-01 | 9.90492880344390869e-01 | 9.90492891217632453e-01 |
| Maipo | 9.90492891217632399e-01 | 9.90492880344390869e-01 | 9.90492891217632453e-01 |
| Ootpa | 9.90492891217632510e-01 | 9.90492880344390869e-01 | 9.90492891217632453e-01 |
观察发现,Debian 10(buster)和RedHat 8(Ootpa)返回了异常结果,进一步对比发行版的依赖版本:
| 发行版 | g++ | glibc |
|---|---|---|
| stretch | 6.3.0 | 2.24 |
| buster | 8.3.0 | 2.28 |
| bullseye | 10.2.1 | 2.31 |
| bookworm | 11.3.0 | 2.33 |
| xenial | 5.4.0 | 2.23 |
| focal | 9.4.0 | 2.31 |
| jammy | 11.2.0 | 2.35 |
| Maipo | 4.8.5 | 2.17 |
| Ootpa | 8.5.0 | 2.28 |
这两个发行版的glibc版本均为2.28,其他版本glibc返回结果一致。
原因分析
这是glibc 2.28版本中exp()函数(针对double类型)的精度bug。在该版本中,针对特定输入值的指数计算存在舍入误差,导致结果与其他版本偏离。后续glibc版本(如2.31及以上)已经修复了这个问题。
解决方法
- 升级glibc版本:如果系统允许,直接升级到glibc 2.31或更高版本,即可解决该精度问题。
- 替换数学库:如果无法升级系统glibc,可以链接独立的数学库(如OpenLibm)替代glibc的数学实现,编译时指定链接该库。
- 手动修正计算:对于该特定输入场景,可以通过调整计算方式(如使用泰勒展开近似,或对结果进行微小修正)来匹配预期值,但这种方法仅适用于特定场景,通用性较差。
- 调整编译选项:尝试添加编译选项
-fno-builtin-exp强制使用glibc的实现而非编译器内联版本;或评估业务需求后使用-ffast-math(注意该选项会牺牲部分精度换取性能)。
内容的提问来源于stack exchange,提问作者URaoul
相关产品推荐
相关产品推荐

