12

On Visual Studio 2010 the following program

#include <iostream>
using std::cout;

int main()
{
    cout << -2147483646 << '\n';
    cout << -2147483647 << '\n';
    cout << -2147483648 << '\n';    // numeric_limits<int>::min()
    cout << -2147483649 << '\n';
    cout << -2147483650 << '\n';
    cout << "..." << '\n';
    cout << -4294967293 << '\n';
    cout << -4294967294 << '\n';
    cout << -4294967295 << '\n';    // -numeric_limits<unsigned int>::max()
    cout << -4294967296 << '\n';
    cout << -4294967297 << '\n';
}

generates the following output

-2147483646
-2147483647
2147483648
2147483647
2147483646
...
3
2
1
-4294967296
-4294967297

What is going on?

Is this standard behavior or a Visual Studio bug?

Edit: As several people have pointed out, there is no such thing as a negative integer literal. See Keith Thompson's excellent answer below for more details.

Johan Råde
  • 20,480
  • 21
  • 73
  • 110
  • Interesting, here's disassembly : cout << -4294967293 << '\n'; push 0Ah mov esi,esp push 3 Notice it pushes 3 as an immediate right away. (VS2010 ultimate) – ScarletAmaranth Dec 14 '11 at 21:09
  • The stream operator of `std::cout` probably is not promoting those literals as you'd expect. – AJG85 Dec 14 '11 at 21:10
  • @ScarletAmaranth I think that's ok, because 4294967293 is first read as unsigned int and than negated which yields 3. Not quite sure though – Gunther Piez Dec 14 '11 at 21:19
  • @drhirsch: he isn't using `numeric_limits::min()` he's using hard coded literals which will be whatever the compiler uses by default. – AJG85 Dec 14 '11 at 21:21
  • He wrote the `numeric_limits` as comments to make clear what the compiler uses, and his values were correct – Gunther Piez Dec 14 '11 at 21:25
  • @drhirsch: yes the values were correct but the types were not what the compiler was using as Keith's answer describes below. – AJG85 Dec 14 '11 at 21:42
  • using hex allows for any integer to be parsed as an int –  Jul 15 '20 at 21:52

2 Answers2

17

-2147483648, for example, is not an integer literal. It's an expression consisting of a unary - operator applied to the literal 2147483648.

Prior to the C++ 2011 standard, C++ didn't require the existence of any type bigger than 32 bits (C++2011 added long long, which is at least 64 bits), so the literal 2147483648 was non-portable.

A decimal integer literal is of the first of the following types in which its value fits:

int
long int
long long int (C++ 2011 and later)

Note that it's never of an unsigned type in standard C++. In pre-2011 editions of the C++ standard (which didn't have long long int), a decimal integer literal that's too big to fit in long int resulted in undefined behavior -- but you're unlikely to have to worry about that. In C++2011 and later, if a decimal integer literal doesn't fit in long long int, then the program is "ill-formed".

Some very old versions of g++ didn't implement the C++2011 semantics. For example, g++ 4.6.1 (the version on which I based the original version of this answer) treated the literal 2147483648 as a constant of type unsigned long. (See the edit history of this question for more out-of-date information.)

Here's a small program that you can use to show how your compiler treats literals:

#include <iostream>
#include <climits>

const char *type_of(int)                { return "int"; }
const char *type_of(unsigned int)       { return "unsigned int"; }
const char *type_of(long)               { return "long"; }
const char *type_of(unsigned long)      { return "unsigned long"; }
const char *type_of(long long)          { return "long long"; }
const char *type_of(unsigned long long) { return "unsigned long long"; }

int main()
{
    std::cout << "int: " << INT_MIN << " .. " << INT_MAX << "\n";
    std::cout << "long: " << LONG_MIN << " .. " << LONG_MAX << "\n";
    std::cout << "long long: " << LLONG_MIN << " .. " << LLONG_MAX << "\n";

    std::cout << "2147483647 is of type " << type_of(2147483647) << "\n";
    std::cout << "2147483648 is of type " << type_of(2147483648) << "\n";
    std::cout << "-2147483647 is of type " << type_of(-2147483647) << "\n";
    std::cout << "-2147483648 is of type " << type_of(-2147483648) << "\n";
}

When I compile and execute it (with g++ 11.3.0 on 64-bit Ubuntu) I get:

int: -2147483648 .. 2147483647
long: -9223372036854775808 .. 9223372036854775807
long long: -9223372036854775808 .. 9223372036854775807
2147483647 is of type int
2147483648 is of type long
-2147483647 is of type int
-2147483648 is of type long
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • 2
    But, as you just explained, an integer literal is never unsigned. 2147483648 should have type `long long` here. – Alan Stokes Dec 14 '11 at 21:30
  • @Keith: You are contradicting yourself: "Note that it's never of an unsigned type" and "takes the unsigned int value 2147483648". Could you please clarify? – Johan Råde Dec 14 '11 at 21:48
  • @user763305: Or if it's `int`, `long`, `unsigned long`, ..., which I think is what gcc does. I suppose I should try this with VS2010. – Keith Thompson Dec 14 '11 at 22:30
  • @user763305: Same output with VS2010. – Keith Thompson Dec 14 '11 at 22:39
  • @KeithThompson: what if you specify "-std=c++11" or "-std=gnu++11" in the command line when compiling? "-std=c99" helps with similar C code. – Alexey Frunze Dec 14 '11 at 22:56
  • @Alex: g++ 4.5.2 doesn't support `-std=c++11`, just `-std=c++0x`. Newer versions of gcc might support it; I'll fire up a newer system and try it. – Keith Thompson Dec 14 '11 at 23:00
  • @KeithThompson: OK. I just noticed that you really need to make gcc operate according to the latest standard, else you get odd results: [C code, some defaults](http://ideone.com/DM7if) vs [C code, C99 strict](http://ideone.com/IwiZE). – Alexey Frunze Dec 14 '11 at 23:03
  • @Alex: I just tried it with gcc 4.6.1, with the same results. It still doesn't have a `-std=c++2011` option, but it has the same behavior with `-std=c++0x`. – Keith Thompson Dec 14 '11 at 23:57
  • This answer was out of date, with information about a version of g++ that's now more than a decade old. I've updated it with more modern information. See the edit history if you're curious about the original. – Keith Thompson Mar 15 '23 at 02:26
2

When I compile this in GCC, I get the following message:

warning: this decimal constant is unsigned only in ISO C90 [enabled by default]

It occurs for every line after (and including)

cout << -2147483648 << '\n';    // numeric_limits<int>::min()

So what's happening is both Visual Studio's compiler and GCC allow us to write these literals, and they just treat them as though there were marked as unsigned. This explains the behaviour of what gets printed, and it makes me pretty confident that the output is correct (supposing we had placed a u suffix on the numbers).

I still find it interesting that -2147483648 is not a valid signed integer literal, even though it is a valid signed integer value. Any thought on that anyone?

Ken Wayne VanderLinde
  • 18,915
  • 3
  • 47
  • 72
  • 4
    `-2147483648` is not a signed integer literal at all. `2147483648` is an integer literal (constant, really), and `-` is the unary minus operator. – James McNellis Dec 14 '11 at 21:20
  • Possibly because it is read as `2147483648`, which exists as unsigned only, and then negated. – Gunther Piez Dec 14 '11 at 21:21
  • @James: Thanks for not. I was not aware that `-` is not a part of the literal – Ken Wayne VanderLinde Dec 14 '11 at 21:23
  • Try specifying "-std=c++11" or "-std=gnu++11" for C++ code or "-std=c99" for C code in the command line when compiling. That should help (it certainly helps with C, but my C++ compilers are too old to validate). – Alexey Frunze Dec 14 '11 at 22:54