C/C++ 中 pow() 函数的实现是否因平台或编译器而异?

问题描述 投票:0回答:3

花了一天时间调试内置

pow()
函数的输出。我的编译器和在线编译器的输出不同。那是一个很长的故事。我写了以下最小、完整和可验证的示例重现了这种情况。

代码:

#include <bits/stdc++.h>
using namespace std;

// This function just prints the binary representation as it is in memory
// A modified version of Lightness Races in Orbit's code given here: https://stackoverflow.com/a/37861479/3555000
// I thank Lightness Races in Orbit for the contribution
void print_binary(long double y)
{
    const long double x = y;
    unsigned char     a[sizeof(long double)];

    copy(
        reinterpret_cast<const unsigned char*>(&x),
        reinterpret_cast<const unsigned char*>(&x) + sizeof(long double),
        &a[0]
    );
    for (auto el : a)
    {
        bitset<8>k(el);
        cout << k.to_string() ;
    }

    cout << endl;
}

int main()
{
    int a[] = {20,29,31}, res=0; //Took three numbers and initialized the result
    for(int i = 0; i<3; i++)
    {
        cout<<"i = "<<i<< ", a["<<i<< "] = "<<a[i]<<"\npow(" << a[i] <<","<<i+1 << "):\nBinary: ";
        long double temp = pow(a[i],i+1);
        print_binary(temp);
        res+=temp;
        cout<<setprecision(50)<<fixed<< "Decimal: " <<temp <<", Result = "<<res<<endl;
    }
    return 0;
}

我的代码中的输出::块:

i = 0, a[0] = 20
pow(20,1):
Binary: 000000000000000000000000000000000000000000000000000000001010000000000011010000000110100000000000
Decimal: 20.00000000000000000000000000000000000000000000000000, Result = 20
i = 1, a[1] = 29
pow(29,2):
Binary: 111111011111111111111111111111111111111111111111001111111101001000001000010000000110100000000000
Decimal: 840.99999999999999983346654630622651893645524978637695, Result = 860
i = 2, a[2] = 31
pow(31,3):
Binary: 111111101111111111111111111111111111111111111111101111011110100000001101010000000110100000000000
Decimal: 29790.99999999999999644728632119949907064437866210937500, Result = 30650

Ideone中的输出:

i = 0, a[0] = 20
pow(20,1):
Binary: 000000000000000000000000000000000000000000000000000000001010000000000011010000000000000000000000
Decimal: 20.00000000000000000000000000000000000000000000000000, Result = 20
i = 1, a[1] = 29
pow(29,2):
Binary: 000000000000000000000000000000000000000000000000010000001101001000001000010000000000000000000000
Decimal: 841.00000000000000000000000000000000000000000000000000, Result = 861
i = 2, a[2] = 31
pow(31,3):
Binary: 000000000000000000000000000000000000000000000000101111101110100000001101010000000000000000000000
Decimal: 29791.00000000000000000000000000000000000000000000000000, Result = 30652

我认为

pow()
函数有时会给出错误的输出,但它的实现在所有编译器中都是相同的。因为我认为它有一个既定的标准。

我的问题:

  • C/C++ 中
    pow()
    函数的实现是否因平台或编译器而异?
  • pow()
    功能没有既定标准吗?
  • 我该如何称呼这个错误? (比如说,未定义的行为)
c++ precision terminology pow
3个回答
3
投票

影响浮点运算结果的因素有很多,例如舍入、运算顺序等。即使使用相同的

pow()
代码,不同的硬件、不同的编译器甚至不同的编译选项也可能会给出不同的结果,这就是为什么二进制比较没有意义。

std::pow
和浮点数有标准。

http://en.cppreference.com/w/cpp/numeric/math/pow

https://en.m.wikipedia.org/wiki/IEEE_floating_point

但正如一些问题评论所指出的,标准并未指定所有内容,编译器/代码有时甚至不遵循标准。大多数时候,这是精度和速度之间的平衡。

数值误差/浮点精度误差/准确度误差 - 不确定。未定义的行为通常是意想不到的并且远离正确。但不同 pow() 的所有结果都是正确的。


0
投票

另一个答案本身并没有错,但可以更具体。 很简单,C++ 标准明确对浮点运算或数字库函数(如

std::pow

)的精度没有任何要求。

各种标准库实现(glibc、musl 等)可以并且确实使用不同的多项式来近似数值函数。以下是 

std::pow

的一些实现,例如:


    libm
  • glibc 目前使用 ARM 提供的相同实现,该实现声称最坏情况错误为 0.54 ULP。
  • glibc 之前(2018 年 9 月之前)的实现
  • 由 IBM 贡献,声称已正确舍入。 来自 Sun Microsystems 的
  • fdlibm
  • ,“几乎四舍五入。”
  • 如果您需要确定性行为,则需要在所有平台/所有编译器上使用相同的标准库。


-3
投票

将 res = 0 移入 for 循环。

© www.soinside.com 2019 - 2024. All rights reserved.