1

__int128 can be used to represent 16 byte numbers, i.e:

__int128 a = -1;

Will set a to 128 bits of 1. However the following does not compile:

int main(){
  __int128 a = 113427455640312821154458202477256070485;
  return 0;
}

With the error:

error: integer literal is too large to be represented in any integer type

Why is the number unable to b represented although it's 127 bits wide?

1 Answers1

0

Because an integer constant in C is treated as, from the standard, "the first of the corresponding list in which its value can be represented".

The list for a non-suffixed decimal integer is:

  • int;
  • long int; and
  • long long int.

Unless your long long int types are the full 128 bits, that value won't fit. The standard allows for an implementation to fix this by virtue of the text (my emphasis):

If an integer constant cannot be represented by any type in its list, it may have an extended integer type, if the extended integer type can represent its value.

However, the use of the word "may" means that an implementation is not required to do this. You'd think it would make sense for a compiler with an extended type to provide a way of creating a constant of that type(1). Unfortunately, gcc appears not to do this, meaning you'll have to do some trickery along the lines of (check those digit counts, I'm not guaranteeing they're correct, just showing you the ugly nature of the workaround):

// long long int can go to 9223372036854775807,
// so break into chunks of 15.
__int128 a = 113427455;
a *=  1000000000000000; // 1e15
a +=   640312821154458;
a *=  1000000000000000;
a +=   202477256070485;

Alternatively, if you want to keep the number together and don't mind a small performance hit, you can use a function to build it at runtime, as per the example program below:

int Mk128(char *str, __int128 *p128) {
    // Handle sign.

    __int128 mult = 1;
    if (*str == '-') {
        mult = -1;
        str++;
    }

    // Collect digits into number, handling grouping.

    *p128 = 0;
    while (*str != '\0') {
        if (*str >= '0' && *str <= '9') {
            *p128 = *p128 * 10 + *str - '0';
        } else if (*str != ',' && *str != ' ' && *str != '.') {
            return 0;
        }
        str++;
    }

    // Adjust for sign and return.

    *p128 *= mult;
    return 1;
}

#include <stdio.h>

// Recursive printer for large numbers, with grouping.

void print128(__int128 x, char *sep) {
     // Handle negatives.

    if (x < 0) {
        putchar('-');
        print128(-x, sep);
        return;
    }

    // Print the top section, we're on the last recursion.

    if (x < 1000) {
        printf("%d", (int)x);
        return;
    }

    // Otherwise recurse, then print this grouping.

    print128(x / 1000, sep);
    printf("%s%03d", sep, (int)(x % 1000));
}

// Test harness, just provide test data as arguments.

int main(int argc, char *argv[]){
    __int128 a;
    puts("=====");
    for (int i = 1; i < argc; i++) {
        puts(argv[i]);
        if (! Mk128(argv[i], &a)) {
            puts("*** Failed to make big int");
        } else {
            print128(a, ",");
            putchar('\n');
        }
        puts("=====");
    }

    return 0;
}

This allows a string to be used with the following properties:

  • It can have either no, or one, leading - sign. If you want a positive number, just leave it off the -, no leading + allowed. This is basically how most people would write an integer constant in their code.

  • It can have spaces, commas, or periods, interspersed with the digits in any manner, but intended to allow for grouping of digits as per the sample run below. The periods are to cater for the Euro habit of swapping commas and periods for grouping and decimal points.

It also has a recursive print function so you can test it, used by the main function to show that the evaluation of the strings has worked.

A sample run:

pax:~> ./testprog 12345 -77 '-12.345,111,111.678.734.333,222 555 777' hello
=====
12345
12,345
=====
-77
-77
=====
-12.345,111,111.678.734.333,222 555 777
-12,345,111,111,678,734,333,222,555,777
=====
hello
*** Failed to make big int
=====

(1) There are some subtle interactions in the standard that make this difficult. Specifically, the [u]intmax_t types are supposed to be able to handle any of the integral types so, in cases where they remain at 64-bit, the 128-bit value is not actually considered an extended integer type, but rather a non-standard extension.

GCC actually calls this out in their documentation:

GNU C provides several language features not found in ISO standard C.

If it was an extended integer type, it would be covered by the standard.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    cppreference.com [says](https://en.cppreference.com/w/cpp/language/integer_literal) *"If the value of the integer literal is too big to fit in any of the types allowed by suffix/base combination and the compiler supports extended integer types (such as __int128) the literal may be given the extended integer type -- otherwise the program is ill-formed."* – Aykhan Hagverdili Jun 01 '20 at 01:51
  • @Ayxan, that *allows* an implementation to do it but it does not *require* it to do so. Based on the latest `gcc` I have (9.3), it doesn't do this. Modified the answer to clarify this. – paxdiablo Jun 01 '20 at 02:13
  • [Here](https://gcc.godbolt.org/z/gUiG8W)'s a `__128` literal. It's C++ though, not C – Aykhan Hagverdili Jun 01 '20 at 02:18
  • 1
    The compiler is not allowed to treat `__int128` as an extended integer type if the ABI it's targeting has `intmax_t` as a 64-bit type, since doing so would contradict the definition of `intmax_t`. Instead `__int128` has to be an extension outside the scope of the language specification. – R.. GitHub STOP HELPING ICE Jun 01 '20 at 02:51
  • Thanks for that, @R, have incorporated it into the answer. – paxdiablo Jun 01 '20 at 03:27
  • From the [gcc manual](https://gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html): "There is no support in GCC for expressing an integer constant of type __int128 for targets with long long integer less than 128 bits wide. " – Nate Eldredge Jun 01 '20 at 03:31