31

When I try to assign an 128-bit integer in gcc 4.9.1, I get a warning: integer constant is too large for its type.

Example Code

int main(void) {
  __uint128_t p = 47942806932686753431;

  return 0;
}

Output

I'm compiling with gcc -std=c11 -o test test.c and I get:

test.c: In function ‘main’:
test.c:2:19: warning: integer constant is too large for its type
   __uint128_t p = 47942806932686753431;
               ^

Am I doing something wrong or is this a bug in gcc?

phuclv
  • 37,963
  • 15
  • 156
  • 475
iblue
  • 29,609
  • 19
  • 89
  • 128

4 Answers4

35

Am I doing something wrong or is this a bug in gcc?

The problem is in 47942806932686753431 part, not in __uint128_t p. According to gcc docs there's no way to declare 128 bit constant:

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.

So, it seems that while you can have 128 bit variables, you cannot have 128 bit constants, unless your long long is 128 bit wide.

The workaround could be to construct 128 bit value from "narrower" integral constants using basic arithmetic operations, and hope for compiler to perform constant folding.

  • 2
    Maybe "cannot have 128 bit integer constants, unless your `intmax_t` is at least 128 bit wide"? – chux - Reinstate Monica Jul 16 '15 at 18:07
  • I'm not sure. The quoted docs mention "long long integer" type specifically. – el.pescado - нет войне Jul 16 '15 at 18:14
  • In reader the C11 draft spec, it is clear that _integer constants_ must have at least the range of `long long/unsigned long long`. It is not clear if _integer constants_ must have at least the range of `intmax_t/uintmax_t`. I would _think_ it would be required. – chux - Reinstate Monica Jul 16 '15 at 18:32
  • @chux, these types are extensions and not covered by the standard. For the `intmax_t` types the standard provides the corresponding macros `INTMAX_C` that are guaranteed to create constants of the correct type. – Jens Gustedt Jul 16 '15 at 18:48
  • 5
    @el.pescado, while there is no direct support, you still can have constant expressions of that type. E.g `((__int128_t)1000000000000*HIGH)+LOW` could be a way of constructing such an expression for a large value, where `HIGH` and `LOW` are the higher and lower digits of the number. – Jens Gustedt Jul 16 '15 at 18:51
  • @Jens Gustedt Macro `INTMAX_C(value)` requires `value` to be an _unsuffixed integer constant_. So it does not make a _integer constant_ out of something that was not all ready an _integer constant_. `INTMAX_C()` does provide a type conversion. So _if_ _integer constants_ are limited to the range of `long long`, `INTMAX_C()` will not help create a constant of the range of `intmax_t`. – chux - Reinstate Monica Jul 16 '15 at 19:59
  • @chux, this refers to 6.4.4.1 and says "integer constant" and not constant of some integer type. So it refers to the lexical definition that is given there. The only constraint that standard imposes on `[U]INTMAX_C` is that the value (!) must not exceed what is representable in the target type. – Jens Gustedt Jul 17 '15 at 08:33
  • @Jens Gusted Agree and thank-you. Minor correction that an additional constraint exist §7.20.4 2 imposes value "... shall be an unsuffixed integer constant". So it can be "123", but not "123LL". – chux - Reinstate Monica Jul 17 '15 at 13:42
8

Have you tried this?

__int128 p = *(__int128*) "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";

EDIT Nov. 25

Sorry for the poor clarification on previous post. Seriously, I didn't post this answer as a joke. Though the GCC doc states there's no way to express a 128-bit integer constant, this post simply provides a workaround for those who wants to assign values to __uint128_t variables with ease.

You may try to compile the code below with GCC (7.2.0) or Clang (5.0.0). It prints desired results.

#include <stdint.h>
#include <stdio.h>

int main()
{
    __uint128_t p = *(__int128*) "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
    printf("HIGH %016llx\n", (uint64_t) (p >> 64));
    printf("LOW  %016llx\n", (uint64_t) p);
    return 0;
}

The stdout:

HIGH 0f0e0d0c0b0a0908
LOW  0706050403020100

This is only regarded as a workaround since it plays tricks on pointers by placing the "value" in .rodata section (if you objdump it), and it's not portable (x86_64 and aarch64 are fine but not arm and x86). I think it's been enough for those coding on desktop machines.

Ryan H.
  • 2,543
  • 1
  • 15
  • 24
jerry73204
  • 141
  • 3
  • 4
  • 3
    Is this intended to be a joke? – Lightness Races in Orbit Oct 25 '15 at 15:29
  • 6
    Even assuming the string value is correct for whatever endianness is needed, that will likely result in a `SIGBUS` for any architecture that has alignment restrictions on `__int128` variables. – Andrew Henle Oct 25 '15 at 15:33
  • 1
    I found it actually works for gcc 5.2.0 on x86_64. Even with `int n = *(int*) "1234";`. Maybe Andrew is right. The string constant is not guaranteed to be aligned and thus this trick may not work on archs other than x86. – jerry73204 Oct 27 '15 at 07:05
  • @jerry73204 : in my case, the compiler is complaining in `C99` about the pointer with `error: expression must have a constant value __int128 llong_min=(*((char *){"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfd"})); ^` – user2284570 Jun 15 '20 at 23:58
  • 2
    Apart from alignment, the solution in this answer also has problems with strict aliasing, but something like `__uint128_t q = (union { unsigned char r[16]; __uint128_t i; }) {00,01,02,03,04,05,06,07,8,9,0xa,0xb,0xc,0xd,0xe,0xf}.i;` works. It assumes the endianness and the width of `char` as 8 bits, but so does the answer above this comment. – Pascal Cuoq Oct 19 '20 at 11:20
2

I'd suggest a simple macro to combine two 64-bit values:

#define UINT128(hi, lo) (((__uint128_t) (hi)) << 64 | (lo))

Example usage:

#include <stdint.h>
#include <stdio.h>

#define UINT128(hi, lo) (((__uint128_t) (hi)) << 64 | (lo))

int main() {
  // Creates constant 0x010203040506070890a0b0c0d0e0f10
  __uint128_t x = UINT128(0x0102030405060708, 0x090a0b0c0d0e0f10);

  // Prints 1, 2, 3, 4, .. 15, 16
  for (int i = 0; i < 16; ++i) {
    printf("%d ", (int)((x >> (120 - 8*i)) & 255));
  }
  printf("\n");
}

Alternatively, you could move the 0x prefix into the macro (not recommended):

#define UINT128(hi, lo) (((__uint128_t) (0x##hi)) << 64 | (0x##lo))

So you can do UINT128(0102030405060708, 090a0b0c0d0e0f10) which is slightly shorter, but that might mess up syntax highlighting, because 0102030405060708 could be misinterpreted as an octal constant, and 090a0b0c0d0e0f10 is not a valid token by itself, which is why I wouldn't recommend this.

Reza Hajianpour
  • 707
  • 1
  • 10
  • 21
Maks Verver
  • 661
  • 4
  • 14
1

I had same issue and cooked up a solution using user-defined literals. Here is how you instantiate the user-defined literal _xxl:

int main(int argc, char** argv) {

    auto a = 0xF0000000000000000000000000000000LLU;
    auto b = 0xF0000000000000000000000000000000_xxl;
    
    printf("sizeof(a): %zu\n", sizeof(a));
    printf("sizeof(b): %zu\n", sizeof(b));

    printf("a == 0? %s\n", a==0 ? "true":"false");
    printf("b == 0? %s\n", b==0 ? "true":"false");

    printf("b >> 124 = %x\n", b >> 124);

    return 0;
}

Output:

sizeof(a): 8
sizeof(b): 16
a == 0? true
b == 0? false
b >> 124 = f

Here is the implementation for the user-defined literal _xxl

#pragma once

#include <stdint.h>

#ifdef __SIZEOF_INT128__
    using uint_xxl_t = __uint128_t;
    using sint_xxl_t = __int128_t;
    
namespace detail_xxl
{
    constexpr uint8_t hexval(char c) 
    { return c>='a' ? (10+c-'a') : c>='A' ? (10+c-'A') : c-'0'; }

    template <int BASE, uint_xxl_t V>
    constexpr uint_xxl_t lit_eval() { return V; }
    
    template <int BASE, uint_xxl_t V, char C, char... Cs>
    constexpr uint_xxl_t lit_eval() {
        static_assert( BASE!=16 || sizeof...(Cs) <=  32-1, "Literal too large for BASE=16");
        static_assert( BASE!=10 || sizeof...(Cs) <=  39-1, "Literal too large for BASE=10");
        static_assert( BASE!=8  || sizeof...(Cs) <=  44-1, "Literal too large for BASE=8");
        static_assert( BASE!=2  || sizeof...(Cs) <= 128-1, "Literal too large for BASE=2");
        return lit_eval<BASE, BASE*V + hexval(C), Cs...>();
    }
    
    template<char... Cs > struct LitEval 
    {static constexpr uint_xxl_t eval() {return lit_eval<10,0,Cs...>();} };

    template<char... Cs> struct LitEval<'0','x',Cs...> 
    {static constexpr uint_xxl_t eval() {return lit_eval<16,0,Cs...>();} };

    template<char... Cs> struct LitEval<'0','b',Cs...> 
    {static constexpr uint_xxl_t eval() {return lit_eval<2,0,Cs...>();} };

    template<char... Cs> struct LitEval<'0',Cs...> 
    {static constexpr uint_xxl_t eval() {return lit_eval<8,0,Cs...>();} };
    
    template<char... Cs> 
    constexpr uint_xxl_t operator "" _xxl() {return LitEval<Cs...>::eval();}
}

template<char... Cs> 
constexpr uint_xxl_t operator "" _xxl() {return ::detail_xxl::operator "" _xxl<Cs...>();}
    
#endif // __SIZEOF_INT128__

It can be used in constexpr just like normal integer constants:

static_assert(   0_xxl == 0, "_xxl error" );
static_assert( 0b0_xxl == 0, "_xxl error" );
static_assert(  00_xxl == 0, "_xxl error" );
static_assert( 0x0_xxl == 0, "_xxl error" );

static_assert(   1_xxl == 1, "_xxl error" );
static_assert( 0b1_xxl == 1, "_xxl error" );
static_assert(  01_xxl == 1, "_xxl error" );
static_assert( 0x1_xxl == 1, "_xxl error" );

static_assert(      2_xxl == 2, "_xxl error" );
static_assert(   0b10_xxl == 2, "_xxl error" );
static_assert(     02_xxl == 2, "_xxl error" );
static_assert(    0x2_xxl == 2, "_xxl error" );

static_assert(      9_xxl == 9, "_xxl error" );
static_assert( 0b1001_xxl == 9, "_xxl error" );
static_assert(    011_xxl == 9, "_xxl error" );
static_assert(    0x9_xxl == 9, "_xxl error" );

static_assert(     10_xxl == 10, "_xxl error" );
static_assert(    0xa_xxl == 10, "_xxl error" );
static_assert(    0xA_xxl == 10, "_xxl error" );

static_assert( 0xABCDEF_xxl == 0xABCDEF, "_xxl error" );
static_assert( 1122334455667788_xxl == 1122334455667788LLu, "_xxl error" );
static_assert(0x80000000000000000000000000000000_xxl >> 126 == 0b10, "_xxl error");
static_assert(0x80000000000000000000000000000000_xxl >> 127 == 0b01, "_xxl error");
static_assert( 0xF000000000000000B000000000000000_xxl > 0xB000000000000000, "_xxl error" );
phuclv
  • 37,963
  • 15
  • 156
  • 475