Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

long long value in Visual Studio

Tags:

c++

long-long

We know that -2*4^31 + 1 = -9.223.372.036.854.775.807, the lowest value you can store in long long, as being said here: What range of values can integer types store in C++. So I have this operation:

#include <iostream>

unsigned long long pow(unsigned a, unsigned b) { 
    unsigned long long p = 1; 
    for (unsigned i = 0; i < b; i++) 
        p *= a; 
    return p; 
}

int main()
{
    long long nr =  -pow(4, 31) + 5 -pow(4,31);
    std::cout << nr << std::endl;
}

Why does it show -9.223.372.036.854.775.808 instead of -9.223.372.036.854.775.803? I'm using Visual Studio 2015.

like image 407
Peter Avatar asked Dec 08 '16 15:12

Peter


2 Answers

Visual Studio has defined pow(double, int), which only requires a conversion of one argument, whereas your pow(unsigned, unsigned) requires conversion of both arguments unless you use pow(4U, 31U). Overloading resolution in C++ is based on the inputs - not the result type.

like image 25
Hans Olsson Avatar answered Oct 06 '22 23:10

Hans Olsson


This is a really nasty little problem which has three(!) causes.

Firstly there is a problem that floating point arithmetic is approximate. If the compiler picks a pow function returning float or double, then 4**31 is so large that 5 is less than 1ULP (unit of least precision), so adding it will do nothing (in other words, 4.0**31+5 == 4.0**31). Multiplying by -2 can be done without loss, and the result can be stored in a long long without loss as the wrong answer: -9.223.372.036.854.775.808.

Secondly, a standard header may include other standard headers, but is not required to. Evidently, Visual Studio's version of <iostream> includes <math.h> (which declares pow in the global namespace), but Code::Blocks' version doesn't.

Thirdly, the OP's pow function is not selected because he passes arguments 4, and 31, which are both of type int, and the declared function has arguments of type unsigned. Since C++11, there are lots of overloads (or a function template) of std::pow. These all return float or double (unless one of the arguments is of type long double - which doesn't apply here).

Thus an overload of std::pow will be a better match ... with a double return values, and we get floating point rounding.

Moral of the story: Don't write functions with the same name as standard library functions, unless you really know what you are doing!

like image 166
Martin Bonner supports Monica Avatar answered Oct 06 '22 22:10

Martin Bonner supports Monica