Does the implementation of pow() function in C/C++

2020-06-29 08:29发布

问题:

It took a day to debug the built-in pow() function's output. The output differed between my compiler and an online compiler. That is a long story. I have written the following Minimal, Complete, and Verifiable example reproduce the situation.

Code:

#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;
}

Output in my Code::Blocks:

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

Output in 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

I thought that pow() function gives wrong output sometimes, but the implementation of it is the same in all compilers. Because I thought it has an established standard.

My Questions:

  • Does the implementation of pow() function in C/C++ vary with platform or compiler?
  • Isn't there any established standard for pow() function?
  • What do I call this error? (Say, Undefined behavior)

回答1:

A lot of factors affect the result of floating-point arithmetic, such as rounding, order of the operations. Even using same code of pow(), different hardware, different compilers or even different compiling options may give different results, that's why binary comparison is meaningless.

There's standard on std::pow and floating point numbers.

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

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

But as pointed by some of the question comments, the standard does not specify everything and compiler/code sometimes does not even follow the standard. Most of the time, it's a balance between precision and speed.

Numerical error / floating-point precision error / accuracy error - not sure. Undefined behavior is usually unexpected and far away from correct. But all results from different pow() are correct.



回答2:

First thing I'd like to point out is that you are using res to print out your result, you are also adding result together multiple times in the loop, meaning by the second iteration your result is wrong.

Move res = 0 into the for loop.