Why do we define INT_MIN as -INT_MAX - 1? [duplicate] - c

This question already has answers here:
(-2147483648> 0) returns true in C++?
(4 answers)
Closed 8 years ago.
AFAIK this is a standard "idiom"
# define INT_MIN (-INT_MAX - 1)
# define INT_MAX 2147483647
Question: Why is the definition of INT_MIN not as -2147483648?

Because 2147483648 is a long value as it does not fit in an int (in common system with 32-bit int and 64-bit long, on system with 32-bit long it is of type long long). So -2147483648 is of type long, not int.
Remember in C, an unsuffixed decimal integer constant is of the first type int, long or long long where it can be represented.
Also in C -2147483648 is not a integer constant; 2147483648 is an integer constant. -2147483648 is an expression formed with the unary operator - and the integer constant 2147483648.
EDIT: if you are not convinced -2147483648 is not of type int (some people in the comments still seem to doubt), you can try to print this:
printf("%zu %zu\n", sizeof INT_MIN, sizeof -2147483648);
You will most likely end up with:
4 8
on common 32 and 64-bit systems.
Also to follow a comment, I'm talking about recent C Standard: use c99 or c11 dialect to test this. c89 rules for decimal integer constant are different: -2147483648 is of type unsigned long in c89. Indeed in c89 (it is different in c99, see above), a unsuffixed decimal integer constant is of type int, long or unsigned long.
EDIT2: #WhozCraig added another example (but for C++) to show -2147483648 is not of type int.
The following example, though in C++, drives home this point. It was compiled with a 32-bit architecture g++. Note the type info gathered from the passed parameter deduction:
#include <iostream>
#include <climits>
template<typename T>
void foo(T value)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
std::cout << value << '\n';
}
int main()
{
foo(-2147483648);
foo(INT_MIN);
return 0;
}
Output
void foo(T) [T = long long]
-2147483648
void foo(T) [T = int]
-2147483648

Related

Safe low 32 bits masking of uint64_t

Assume the following code:
uint64_t g_global_var;
....
....
void foo(void)
{
uint64_t local_32bit_low = g_global_var & 0xFFFFFFFF;
....
}
With the current toolchain, this code works as expected, local_32bit_low indeed contains the low 32 bits of g_global_var.
I wonder if it is guaranteed by the standard C that this code will always work as expected?
My concern is that the compiler may treat 0xFFFFFFFF as integer value of -1 and when promoting to uint64_t it would become 0xFFFFFFFFFFFFFFFF.
P.S.
I know that to be on the safe side it is better to use 0xFFFFFFFFULL in this case. The point is that I saw it in a legacy code and I wonder if it worth to be fixed or not.
There is no problem. The integer constant 0xFFFFFFFF has the type that is able to store the value as is.
According to the C Standard (6.4.4.1 Integer constants)
5 The type of an integer constant is the first of the corresponding
list in which its value can be represented
So this value is stored as a positive value.
If the type unsigned int is a 32-bit integer type then the constant will have the type unsigned int.
Otherwise it will have one of the types that can store the value.
long int
unsigned long int
long long int
unsigned long long int
Due to the usual arithmetic conversions in the expression
g_global_var & 0xFFFFFFFF;
it is promoted like
0x00000000FFFFFFFF
Pay attention to that in C there is no negative integer constants. For example an expression like
-10
consists of two sub-expressions: the primary expression 10 and the sub-expression with the unary operator - -19 that coincides with the full expression.
0xffffffff is not -1, ever. It may convert to -1 if you cast or coerce (e.g. by assignment) it to a signed 32-bit type, but integer literals in C always have their mathematical value unless they overflow.
For decimal literals, the type is the narrowest signed type that can represent the value. For hex literals, unsigned types are used before going up to the next wider signed type. So, in the common case where int is 32-bit, 0xffffffff would have type unsigned int. If you wrote it as decimal, it would have type long (if long is 64-bit) or long long (if long is only 32-bit).
The type of an unsuffixed hexadecimal or octal constant is the first of the following list in which its value can be represented:
int
unsigned int
long int
unsigned long int
long long int
unsigned long long int
(For unsuffixed decimal constants, remove the unsigned types from the above list.)
The hexadecimal constant 0xFFFFFFFF can definitely be represented by unsigned long int, so its type will be the first of int, unsigned int, long int or unsigned long int that can represent its value.
Note that although 0xFFFFFFFF > 0 always evaluates to 1 (true), it is possible for 0xFFFFFFFF > -1 to evaluate to either 0 (false) or 1 (true) on different implementations. So you need to be careful when comparing integer constants with each other or with other objects of integer type.
Others have answered the question, just a recomendation, next time (if you are under C11) you can check the type of the expression by yourself using _Generic
#include <stdio.h>
#include <stdint.h>
#define print_type(x) _Generic((x), \
int64_t: puts("int64_t"), \
uint64_t: puts("uint64_t"), \
default: puts("unknown") \
)
uint64_t g_global_var;
int main(void)
{
print_type(g_global_var & 0xFFFFFFFF);
return 0;
}
The ouput is
uint64_t

unsigned variable is behaving like signed [duplicate]

This question already has answers here:
Comparison operation on unsigned and signed integers
(7 answers)
In a C expression where unsigned int and signed int are present, which type will be promoted to what type?
(2 answers)
Closed 7 years ago.
This is the code,
#include <stdio.h>
int main()
{
unsigned int i = 0xFFFFFFFF;
if (i == -1)
printf("signed variable\n");
else
printf("unsigned variable\n");
return 0;
}
This is the output,
signed variable
Why is i's value -1 even it is declared as unsigned?
Is it something related to implicit type conversations?
This is the build environment,
Ubuntu 14.04, GCC 4.8.2
The == operator causes its operands to be promoted to a common type according to C's promotion rules. Converting -1 to unsigned yields UINT_MAX.
i's value is 0xFFFFFFFF, which is exactly the same as -1, at least when the later is converted to an unsigned integer. And this is exactly what is happening with the comparison operators:
If both of the operands have arithmetic type, the usual arithmetic conversions are performed. [...]
[N1570 $6.5.9/4]
-1 in two's complement is "all bits set", which is also what 0xFFFFFFFF for an unsigned int (of size 4) is.

Why is -2147483648 automatically promoted to long when it can fit in int?

#include <stdio.h>
int main()
{
printf("%zu\n", sizeof(-2147483648));
printf("%zu\n", sizeof(-2147483647-1));
return 0;
}
The above code gives as output (gcc):
8
4
Why is -2147483648 automatically promoted to long in 1st printf even when it can fit in an int?
Also, I tried the same in MinGW and it gives the output:
4
4
Can someone please explain what's going on?
The number 2147483648 is too large to fit into an int, so it is promoted to long.
Then, after the number has already been promoted to long, its negative is computed, yielding -2147483648.
If you're curious, you can look at limits.h. On my platform, which uses glibc,
# define INT_MIN (-INT_MAX - 1)
# define INT_MAX 2147483647
On MinGW, sizeof(long) == 4, so promotion to long won't cut it. According to the C11 standard, the value must be promoted to long long. This doesn't happen, probably because your MinGW compiler is defaulting to C90 or earlier.
-2147483648 is the integral constant expression 2147483648 with the unary minus operator applied.
Your systems both appear to have int and long as 32-bit, and long long as 64-bit.
Since the base-10 constant 2147483648 cannot fit in long int, in C99 it has type long long int. However, C90 did not have this type, so in C90 it has type unsigned long int.
A common workaround for this issue is, as you say, to write -2147483647 - 1. You will probably see something similar as the definition of INT_MIN if you check your system's limits.h .
Regarding the output 4, this suggests that on your MinGW system you are using C90 and on your other system you are using C99 or C11 (or C++, or GCC extensions).

Can't assign -2147483648 to variable of type long long

I'm compiling the code below and for some reason I can't assign -2147483648 to the variable which is 8 bytes long and signed.
long long x = -2147483648;
When I step over this line, the value of x is 2147483648 and the 'Watch' window in MS Visual Studio shows that the type of x is __int64. A sizeof(x) also returns 8.
According to limit.h the limits for a signed long long are:
#define LLONG_MAX 9223372036854775807i64 /* maximum signed long long int value */
#define LLONG_MIN (-9223372036854775807i64 - 1) /* minimum signed long long int value */
and:
/* minimum signed 64 bit value */
#define _I64_MIN (-9223372036854775807i64 - 1)
/* maximum signed 64 bit value */
#define _I64_MAX 9223372036854775807i64
I just don't get it!!!
Can somebody please shed some light on this?
Without the LL, the compiler appears to deduce 2147483648 is a 32-bit unsigned long. Then it applies the - operator. The result is 0 - 2147483648. Since this is less than 0 and being an unsigned long t, 4294967296 is added, which is 2147483648 again. This value is then assigned to long long x.
Suggest:
long long x = -2147483648LL;
// or
long long x = -2147483647 - 1;
Try assigning to -2147483648LL
see Integer constants here
Your code compiles and executes fine on my GCC 4.6.3 compiler, with --std=c99. I suspect that you are using the rather hopeless so-called C compiler that Microsoft supply. It obviously isn't very clever. Use a long long suffix (i64, ll or LL) to trick it into behaving.
Interestingly the MS C++ compiler cannot get this right either:
#include <iostream>
int main()
{
long long x = -2147483647;
std::cout << x << std::endl;
x = -2147483648;
std::cout << x << std::endl;
x = -2147483649;
std::cout << x << std::endl;
return 0;
}
Output
-2147483647
2147483648
2147483647
I compiled this with the x86 C++ compiler from VS2013.
And I get the same output from my g++ 4.6.3.
So I think there is more to this than meets the eye. I hope somebody that knows more than me could explain all this.
In answer to some of the other comments (sorry, can't reply to each of them as I don't have enough rep yet):
In C and C++ the type of an expression doesn't depend on its context. In this case, the type of -214743648 is defined by the language rules, the fact that you later assign it to a long long doesn't affect this.
Actually this way of doing things makes the language much simpler than the alternative, it's one of the things that attracted me to C in the first place.
In David Heffernan's example,
x = -2147483648;
std::cout << x << std::endl; // gives 2147483648
x = -2147483649;
std::cout << x << std::endl; // gives 2147483647
The important thing is that the - sign is NOT part of an integer literal. The expression 2147483648 is an integer constant whose type is determined according to a set of rules in the standard; and then the unary minus operator is applied to the value (which does not change its type).
Unfortunately, C90, C99, C++98 and C++11 all have different rules for the types of integer literals. Further, the rules are different for decimal constants than for hex or octal constants! You can look them up in the relevant standards documents.
If you have 32-bit ints, then 2147483648 is too large to be an int. In all dialects the next possible type for it is long int. If you also have 32-bit long ints, then in C99 or C++11 it has type long long int. In C90 or C++98 it has type unsigned long int. (Those languages do not have a long long type).
Back to David Heffernan's example. C++98 does not have long long, so either you're using a C++11 compiler, or using Microsoft extensions. Assuming the latter; who knows what they've decided to do for integer constants, but if they have retained the C++98 definition that 2147483648 has type unsigned long int, that would explain the results.
Turns out that I just had to write it like this:
long long x = -2147483648i64;
Why is the compiler not able to figure it out. I already spelled out the type, so why do I have to put 'i64' after the number ???

C: Casting minimum 32-bit integer (-2147483648) to float gives positive number (2147483648.0)

I was working on an embedded project when I ran into something which I thought was strange behaviour. I managed to reproduce it on codepad (see below) to confirm, but don't have any other C compilers on my machine to try it on them.
Scenario: I have a #define for the most negative value a 32-bit integer can hold, and then I try to use this to compare with a floating point value as shown below:
#define INT32_MIN (-2147483648L)
void main()
{
float myNumber = 0.0f;
if(myNumber > INT32_MIN)
{
printf("Everything is OK");
}
else
{
printf("The universe is broken!!");
}
}
Codepad link: http://codepad.org/cBneMZL5
To me it looks as though this this code should work fine, but to my surprise it prints out The universe is broken!!.
This code implicitly casts the INT32_MIN to a float, but it turns out that this results in a floating point value of 2147483648.0 (positive!), even though the floating point type is perfectly capable of representing -2147483648.0.
Does anyone have any insights into the cause of this behaviour?
CODE SOLUTION: As Steve Jessop mentioned in his answer, limits.h and stdint.h contain correct (working) int range defines already, so I'm now using these instead of my own #define
PROBLEM/SOLUTION EXPLANATION SUMMARY: Given the answers and discussions, I think this is a good summary of what's going on (note: still read the answers/comments because they provide a more detailed explanation):
I'm using a C89 compiler with 32-bit longs, so any values greater than LONG_MAX and less or equal to ULONG_MAX followed by the L postfix have a type of unsigned long
(-2147483648L) is actually a unary - on an unsigned long (see previous point) value: -(2147483648L). This negation operation 'wraps' the value around to be the unsigned long value of 2147483648 (because 32-bit unsigned longs have the range 0 - 4294967295).
This unsigned long number looks like the expected negative int value when it gets printed as an int or passed to a function because it is first getting cast to an int, which is wrapping this out-of-range 2147483648 around to -2147483648 (because 32-bit ints have the range -2147483648 to 2147483647)
The cast to float, however, is using the actual unsigned long value 2147483648 for conversion, resulting in the floating-point value of 2147483648.0.
Replace
#define INT32_MIN (-2147483648L)
with
#define INT32_MIN (-2147483647 - 1)
-2147483648 is interpreted by the compiler to be the negation of 2147483648, which causes overflow on an int. So you should write (-2147483647 - 1) instead.
This is all C89 standard though. See Steve Jessop's answer for C99.
Also long is typically 32 bits on 32-bit machines, and 64 bits on 64-bit machines. int here gets the things done.
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 use int32_t and INT32_MIN from that.
if all else fails, write stdint.h yourself, and use the expression in WiSaGaN's answer. It has type int if int is at least 32 bits, otherwise long.

Resources