代码之家  ›  专栏  ›  技术社区  ›  jpo38

为什么我要得到std::exp的平台特定结果?[复制品]

  •  6
  • jpo38  · 技术社区  · 5 年前

    我有一个程序,在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;
    }
    

    在用Visual2015编译的Windows下,我得到了输出:

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

    在android/armv7下,用g++ndk r11b编译,我得到输出:

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

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

    PC:      0.00038114128472300899284561093161
    Android: 0.000381141284723008938635502307335
    

    注意我的程序做了很多数学运算,我只注意到 std::exp 为相同的输入生成不同的结果…并且仅为某些特定的输入值(没有调查这些值是否具有相似的属性),对于大多数输入值,结果是相同的。

    • 这种行为是“预期”的吗?在某些情况下,是否没有保证会有同样的结果?
    • 有什么编译器标记可以修复这个问题吗?
    • 或者我是否需要将我的结果四舍五入,以相同的结果在两个平台上结束?那么四舍五入的好策略是什么呢?因为如果输入的话,E-20处的舍入数组会释放太多的信息 var 非常小?

    编辑:我认为我的问题不是 Is floating point math broken? . 两个平台上的结果完全相同,只有 性病:: 对于某些特定值,会产生不同的结果。

    1 回复  |  直到 5 年前
        1
  •  6
  •   LoPiTaL    5 年前

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

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

    可能微软图书馆使用了不同的计算方法(找不到有关它的信息),从而产生了不同的结果。

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

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

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

    有一些例外,因为 sqrt 功能或 std::fma 一些取整函数和基本的算术运算