Why it is different between -2147483648 and (int)-2147483648
2147483648 doesn't fit into an int or a long on your system, so it's treated as a constant of type unsigned long. (Edit: as ouah pointed out in the comments, it's undefined behaviour in standard C++, but your compiler accepts it as an extension.) Negating an unsigned integer value is possible, but results in another unsigned integer value, never a negative number. Negating 2147483648UL produces 2147483648UL (assuming, as is the case on your system, that unsigned long is a 32 bit type).
Casting that to int
produces an implementation-defined result, commonly the result you see, but not necessarily. You can get the result you want without any conversions by writing -2147483647 - 1.
Why is -(-2147483648) = - 2147483648 in a 32-bit machine?
Negating an (unsuffixed) integer constant:
The expression -(-2147483648)
is perfectly defined in C, however it may be not obvious why it is this way.
When you write -2147483648
, it is formed as unary minus operator applied to integer constant. If 2147483648
can't be expressed as int
, then it s is represented as long
or long long
* (whichever fits first), where the latter type is guaranteed by the C Standard to cover that value†.
To confirm that, you could examine it by:
printf("%zu\n", sizeof(-2147483648));
which yields 8
on my machine.
The next step is to apply second -
operator, in which case the final value is 2147483648L
(assuming that it was eventually represented as long
). If you try to assign it to int
object, as follows:
int n = -(-2147483648);
then the actual behavior is implementation-defined. Referring to the Standard:
C11 §6.3.1.3/3 Signed and unsigned integers
Otherwise, the new type is signed and the value cannot be represented
in it; either the result is implementation-defined or an
implementation-defined signal is raised.
The most common way is to simply cut-off the higher bits. For instance, GCC documents it as:
For conversion to a type of width N, the value is reduced modulo 2^N
to be within range of the type; no signal is raised.
Conceptually, the conversion to type of width 32 can be illustrated by bitwise AND operation:
value & (2^32 - 1) // preserve 32 least significant bits
In accordance with two's complement arithmetic, the value of n
is formed with all zeros and MSB (sign) bit set, which represents value of -2^31
, that is -2147483648
.
Negating an int
object:
If you try to negate int
object, that holds value of -2147483648
, then assuming two's complement machine, the program will exhibit undefined behavior:
n = -n; // UB if n == INT_MIN and INT_MAX == 2147483647
C11 §6.5/5 Expressions
If an exceptional condition occurs during the evaluation of an
expression (that is, if the result is not mathematically defined or
not in the range of representable values for its type), the behavior
is undefined.
Additional references:
- INT32-C. Ensure that operations on signed integers do not result in overflow
*) In withdrawed C90 Standard, there was no long long
type and the rules were different. Specifically, sequence for unsuffixed decimal was int
, long int
, unsigned long int
(C90 §6.1.3.2 Integer constants).
†) This is due to LLONG_MAX
, which must be at least +9223372036854775807
(C11 §5.2.4.2.1/1).
Why does the smallest int, −2147483648, have type 'long'?
In C, -2147483648
is not an integer constant. 2147483648
is an integer constant, and -
is just a unary operator applied to it, yielding a constant expression. The value of 2147483648
does not fit in an int
(it's one too large, 2147483647
is typically the largest integer) and thus the integer constant has type long
, which causes the problem you observe. If you want to mention the lower limit for an int
, either use the macro INT_MIN
from <limits.h>
(the portable approach) or carefully avoid mentioning 2147483648
:
printf("PRINTF(d) \t: %d\n", -1 - 2147483647);
Why (int)pow(2, 32) == -2147483648
Assuming int
is 32 bits (or less) on your machine, this is undefined behavior.
From the standard, conv.fpint:
A prvalue of a floating-point type can be converted to a prvalue of an integer type. The conversion truncates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot be represented in the destination type.
Most commonly int
is 32 bits, and it can represent values in the interval [-2^31, 2^31-1]
which is [-2147483648, 2147483647]
. The result of std::pow(2, 32)
is a double
that represents the exact value 2^32
. Since 2^32
exceeds the range that can be represented by int
, the conversion attempt is undefined behavior. This means that in the best case, the result can be anything.
The same goes for your second example: pow(2, 32) - pow(2, 31)
is simply the double
representation of 2^31
, which (just barely) exceeds the range that can be represented by a 32-bit int
.
The correct way to do this would be to convert to a large enough integral type, e.g. int64_t
:
std::cout << static_cast<int64_t>(std::pow(2, 32)) << "\n"; // prints 4294967296
Largest decimal literal of type int is 2147483648 or 2147483647?
Note that there are no negative integral literals and Integer.MIN_VALUE
is −2147483648. So -2147483648
is parsed as “apply unary minus to 2147483648”. It would be very bad if 2147483648 would not be a valid decimal int
literal or you couldn't use an int
literal of value Integer.MIN_VALUE
directly in your program.
Side note: The JLS defines what is correct. So it is correct by definition. It can be bad, though.
C: Casting minimum 32-bit integer (-2147483648) to float gives positive number (2147483648.0)
In C89 with a 32 bit long
, 2147483648L
has type unsigned long int
(see 3.1.3.2 Integer constants). So once modulo arithmetic has been applied to the unary minus operation, INT32_MIN
is the positive value 2147483648 with type unsigned long
.
In C99, 2147483648L
has type long
if long
is bigger than 32 bits, or long long
otherwise (see 6.4.4.1 Integer constants). So there is no problem and INT32_MIN
is the negative value -2147483648 with type long
or long long
.
Similarly in C89 with long
larger than 32 bits, 2147483648L
has type long
and INT32_MIN
is negative.
I guess you're using a C89 compiler with a 32 bit long
.
One way to look at it is that C99 fixes a "mistake" in C89. In C99 a decimal literal with no U
suffix always has signed type, whereas in C89 it may be signed or unsigned depending on its value.
What you should probably do, btw, is include limits.h
and use INT_MIN
for the minimum value of an int
, and LONG_MIN
for the minimum value of a long
. They have the correct value and the expected type (INT_MIN
is an int
, LONG_MIN
is a long
). If you need an exact 32 bit type then (assuming your implementation is 2's complement):
- for code that doesn't have to be portable, you could use whichever type you prefer that's the correct size, and assert it to be on the safe side.
- for code that has to be portable, search for a version of the C99 header
stdint.h
that works on your C89 compiler, and useint32_t
andINT32_MIN
from that. - if all else fails, write
stdint.h
yourself, and use the expression in WiSaGaN's answer. It has typeint
ifint
is at least 32 bits, otherwiselong
.
Related Topics
Avoiding If Statement Inside a for Loop
What Is the Purpose of Forward Declaration
Why How to Define Structures and Classes Within a Function in C++
Std::Variant' VS. Inheritance VS. Other Ways (Performance)
Getting a List of User Profiles on a Computer in C++ Win32
G++ Linker: Force Static Linking If Static Library Exists
Emacs C++-Mode Incorrect Indentation
Finding Memory Leaks in a C++ Application with Visual Studio
How to Treat a Specific Warning as an Error
C++ Return Value, Reference, Const Reference
How to Set Given Channel of a Cv::Mat to a Given Value Efficiently Without Changing Other Channels
Rc.Exe No Longer Found in VS 2015 Command Prompt
Generate All Sequences of Bits Within Hamming Distance T
How to Profile Multi-Threaded C++ Application on Linux
Can't Use Structure in Global Scope
How to Get Duration, as Int Milli's and Float Seconds from <Chrono>