2

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)
Community
  • 1
  • 1
Enamul Hassan
  • 4,744
  • 22
  • 35
  • 52
  • 1
    [what every computer scientist should know about floating-point arithmetic](http://perso.ens-lyon.fr/jean-michel.muller/goldberg.pdf) – 101010 Jun 16 '16 at 19:34
  • 1
    http://stackoverflow.com/questions/20945815/math-precision-requirements-of-c-and-c-standard – Ian Jun 16 '16 at 19:43
  • Even more fun: on the same compiler there can be subtle differences between `pow` and `std::pow`. I've never been able to figure out why. By the way, `#include using namespace std;` exposes you to unnecessary risk. See here: http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-in-c-considered-bad-practice and here http://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h – user4581301 Jun 16 '16 at 19:49
  • I notice you took my code from your previous question (without attribution), and made it worse. (a) [Don't use that "bits" header](http://stackoverflow.com/q/31816095/560648); (b) Don't write `using namespace std;` (c) Don't use `bitset<8>` for this. – Lightness Races in Orbit Jun 16 '16 at 19:54
  • @LightnessRacesinOrbit At first I did not notice it as it makes almost no importance here. But later I noticed it and started to preparing how I can attribute you. I am extremely sorry. I did not ask your original name. Can you please tell me your name? – Enamul Hassan Jun 16 '16 at 20:00
  • @manetsus: My name is "Lightness Races in Orbit". The attribution is right now, thank you. – Lightness Races in Orbit Jun 16 '16 at 20:48
  • @LightnessRacesinOrbit Really? Is it actually your name? Then what would be your nickname? I don't believe! However, you are welcome and sorry again. :) – Enamul Hassan Jun 16 '16 at 20:55
  • You may be interested in this [Q & A](http://stackoverflow.com/questions/6430448/why-doesnt-gcc-optimize-aaaaaa-to-aaaaaa) too. – Bob__ Jun 16 '16 at 21:26
  • That least-significant-bit down-round on integer results really chaps my hide. – Christopher Oicles Jun 17 '16 at 02:10

2 Answers2

2

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.

kangshiyin
  • 9,321
  • 1
  • 14
  • 28
-3

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.

Joel H
  • 1