Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a 16 byte signed integer data type?"

Tags:

c++

c

types

integer

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?

like image 991
Galaxy Avatar asked Dec 23 '22 06:12

Galaxy


1 Answers

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.)

like image 158
PSkocik Avatar answered Dec 28 '22 06:12

PSkocik