为什么我会得到std :: exp特定于平台的结果?

时间:2019-01-18 08:30:33

标签: c++ rounding-error exp

我有一个程序在Android和Windows下给出了截然不同的结果。当我针对二进制文件包含预期结果验证输出数据时,即使很小(四舍五入的问题),差异也很烦人,我必须找到一种解决方法。

这是一个示例程序:

#include <iostream>
#include <iomanip>
#include <bitset>

int main( int argc, char* argv[] )
{
    // this value was identified as producing different result when used as parameter to std::exp function
    unsigned char val[] = {158, 141, 250, 206, 70, 125, 31, 192};

    double var = *((double*)val);

    std::cout << std::setprecision(30);

    std::cout << "var is " << var << std::endl;
    double exp_var = std::exp(var);
    std::cout << "std::exp(var) is " << exp_var << std::endl;
}

在Windows中,使用Visual 2015编译,得到输出:

var is -7.87234042553191493141184764681
std::exp(var) is 0.00038114128472300899284561093161

在使用g ++ NDK r11b编译的Android / armv7下,我得到了输出:

var is -7.87234042553191493141184764681
std::exp(var) is 0.000381141284723008938635502307335

因此从e-20开始,结果是不同的:

PC:      0.00038114128472300899284561093161
Android: 0.000381141284723008938635502307335

请注意,我的程序执行了大量数学运算,我只注意到std::exp对于相同的输入产生了不同的结果...并且仅对于某些特定的输入值(没有研究这些值是否具有相似的值)属性),对于大多数对象来说,结果是相同的。

  • 这种行为是“预期的”吗,在某些情况下不能保证得到相同的结果吗?
  • 是否有一些可以解决该问题的编译器标志?
  • 还是我需要对结果进行取整以使其在两个平台上都相同?那么什么是四舍五入的好策略?因为如果输入var很小,在e-20处舍入舍入会丢失太多信息?

编辑:我认为我的问题不是Is floating point math broken?的重复。我在两个平台上都得到完全相同的结果,只有std::exp的某些特定值会产生不同的结果。

1 个答案:

答案 0 :(得分:6)

该标准未定义应如何实现exp函数(或任何其他数学库函数 1 ),因此每种库实现都可以使用不同的计算方法。

例如,Android C库(bionic)通过间隔[0,0.34658]上的特殊有理函数使用exp(r)的近似值,并缩小结果。

Microsoft图书馆可能正在使用其他计算方法(无法找到有关它的信息),从而导致不同的结果。

此外,库可以采用动态加载策略(即加载包含实际实现的.dll),以利用不同的硬件特定功能,即使使用相同的编译器,其结果也更加不可预测

为了在两个(所有)平台上获得相同的实现,您可以使用自己的exp函数的实现,因此不必依赖于不同库的不同实现。

考虑到处理器可能采用了不同的舍入方法,这也会产生不同的结果。

1 有一些例外,例如sqrt函数或std::fma以及一些舍入函数和基本算术运算