简体   繁体   中英

long long value in Visual Studio

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.

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!

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.

The lowest long long value can be obtained through numeric_limits . For long long it is:

auto lowest_ll = std::numeric_limits<long long>::lowest();

which results in:

-9223372036854775808

The pow() function that gets called is not yours hence the observed results. Change the name of the function.

The only possible explaination for the -9.223.372.036.854.775.808 result is the use of the pow function from the standard library returning a double value. In that case, the 5 will be below the precision of the double computation, and the result will be exactly -2 63 and converted to a long long will give 0x8000000000000000 or -9.223.372.036.854.775.808 .

If you use you function returning an unsigned long long, you get a warning saying that you apply unary minus to an unsigned type and still get an ULL. So the whole operation should be executed as unsigned long long and should give without overflow 0x8000000000000005 as unsigned value. When you cast it to a signed value, the result is undefined, but all compilers I know simply use the signed integer with same representation which is -9.223.372.036.854.775.803 .

But it would be simple to make the computation as signed long long without any warning by just using:

long long nr =  -1 * pow(4, 31) + 5 - pow(4,31);

As a addition, you have neither undefined cast nor overflow here so the result is perfectly defined per standard provided unsigned long long is at least 64 bits.

Your first call to pow is using the C standard library's function, which operates on floating points. Try giving your pow function a unique name:

unsigned long long my_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 = -my_pow(4, 31) + 5 - my_pow(4, 31);
    std::cout << nr << std::endl;
}

This code reports an error: "unary minus operator applied to unsigned type, result still unsigned". So, essentially, your original code called a floating point function, negated the value, applied some integer arithmetic to it, for which it did not have enough precision to give the answer you were looking for (at 19 digits of presicion!). To get the answer you're looking for, change the signature to:

long long my_pow(unsigned a, unsigned b);

This worked for me in MSVC++ 2013. As stated in other answers, you're getting the floating-point pow because your function expects unsigned , and receives signed integer constants. Adding U to your integers invokes your version of pow .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM