I made this program to test what data types arbitrary integer literals get evaluated to. This program was inspired from reading some other questions on StackOverflow.
How do I define a constant equal to -2147483648?
Why do we define INT_MIN as -INT_MAX - 1?
(-2147483648> 0) returns true in C++?
In these questions, we have an issue: the programmer wants to write INT_MIN as -
2^31, but 2^31 is actually a literal and -
is the unary negation operator. Since INT_MAX is usually 2^31 - 1 having a 32-bit int, the literal 2^31 cannot be represented as an int
, and so it gets promoted to a larger data type. The second answer in the third question has a chart according to which the data type of the integer literals is determined. The compiler goes down the list from the top until it finds a data type which can fit the literal.
Suffix Decimal constants none int long int long long int
=========================================================================
In my little program, I define a macro that will return the "name" of a variable, literal, or expression, as a C-string. Basically, it returns the text that is passed inside of the macro, exactly as you see it in the code editor. I use this for printing the literal expression.
I want to determine the data type of the expression, what it evaluates to. I have to be a little clever about how I do this. How can we determine the data type of a variable or an expression in C? I've concluded that only two "bits" of information are necessary: the width of the data type in bytes, and the signedness of the data type.
I use the sizeof()
operator to determine the width of the data type in bytes. I also use another macro to determine if the data type is signed or not. typeof()
is a GNU compiler extension that returns the data type of a variable or expression. But I cannot read the data type. I typecast -1
to whatever that data type is. If it's a signed data type, it will still be -1
, if it's an unsigned data type, it will become the UINT_MAX
for that data type.
#include <stdio.h> /* C standard input/output - for printf() */
#include <stdlib.h> /* C standard library - for EXIT_SUCCESS */
/**
* Returns the name of the variable or expression passed in as a string.
*/
#define NAME(x) #x
/**
* Returns 1 if the passed in expression is a signed type.
* -1 is cast to the type of the expression.
* If it is signed, -1 < 0 == 1 (TRUE)
* If it is unsigned, UMax < 0 == 0 (FALSE)
*/
#define IS_SIGNED_TYPE(x) ((typeof(x))-1 < 0)
int main(void)
{
/* What data type is the literal -9223372036854775808? */
printf("The literal is %s\n", NAME(-9223372036854775808));
printf("The literal takes up %u bytes\n", sizeof(-9223372036854775808));
if (IS_SIGNED_TYPE(-9223372036854775808))
printf("The literal is of a signed type.\n");
else
printf("The literal is of an unsigned type.\n");
return EXIT_SUCCESS;
}
As you can see, I'm testing -
2^63 to see what data type it is. The problem is that in ISO C90, the "largest" data type for integer literals appears to be long long int
, if we can believe the chart. As we all know, long long int
has a numerical range -2^63 to 2^63 - 1 on a modern 64-bit system. However, the -
above is the unary negation operator, not really part of the integer literal. I'm attempting to determine the data type of 2^63, which is too big for the long long int
. I'm attempting to cause a bug in C's type system. That is intentional, and only for educational purposes.
I am compiling and running the program. I use -std=gnu99
instead of -std=c99
because I am using typeof()
, a GNU compiler extension, not actually part of the ISO C99 standard. I get the following output:
$ gcc -m64 -std=gnu99 -pedantic experiment.c
$
$ ./a.out
The literal is -9223372036854775808
The literal takes up 16 bytes
The literal is of a signed type.
I see that the integer literal equivalent to 2^63 evaluates to a 16 byte signed integer type! As far as I know, there is no such data type in the C programming language. I also don't know of any Intel x86_64 processor that has a 16 byte register to store such an rvalue. Please correct me if I'm wrong. Explain what's going on here? Why is there no overflow? Also, is it possible to define a 16 byte data type in C? How would you do it?
Your platform likely has __int128
and 9223372036854775808
is acquiring that type.
A simple way to get a C compiler to print a typename is with something like:
int main(void)
{
#define LITERAL (-9223372036854775808)
_Generic(LITERAL, struct {char x;}/*can't ever match*/: "");
}
On my x86_64 Linux, the above is generating an
error: ‘_Generic’ selector of type ‘__int128’ is not compatible with any association
error message, implying __int128
is indeed the type of the literal.
(With this, the warning: integer constant is so large that it is unsigned
is wrong. Well, gcc isn't perfect.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With