Asignación de enteros de 128 bits en C

8 minutos de lectura

Cuando trato de asignar un entero de 128 bits en gcc 4.9.1, obtengo un warning: integer constant is too large for its type.

Código de ejemplo

int main(void) {
  __uint128_t p = 47942806932686753431;

  return 0;
}

Producción

estoy compilando con gcc -std=c11 -o test test.c y obtengo:

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

¿Estoy haciendo algo mal o es un error en gcc?

  • Consulte también “¿Por qué no hay int128_t?” responder.

    – chux – Reincorporar a Monica

    16 de julio de 2015 a las 18:13

  • ((__uint128_t)47942806*1000000+932686)*1000000+753431

    – Marc Glisse

    16 de julio de 2015 a las 18:15

  • Por cierto, probablemente deberías reemplazar las instancias de __uint128_t con unsigned __int128. El primero parece estar ‘obsoleto’.

    – Brett Hale

    16 de julio de 2015 a las 19:26

  • “Cuando trato de asignar un número entero de 128 bits” Vaya, no, eso no es lo que estás haciendo. Estás tratando de asignar para un entero de 128 bits, ¡pero lo que le estás asignando no es uno!

    – Carreras de ligereza en órbita

    25/10/2015 a las 15:29

  • ¿Hay números enteros de 128 bits ahora? Derecha. 128 bits. Crecimiento exponencial. ¿Esto tiene que ver con el cifrado?

    –Steve Woods

    13 de julio de 2018 a las 13:20


avatar de usuario
el.pescado – нет войне

¿Estoy haciendo algo mal o es un error en gcc?

el problema esta en 47942806932686753431 parte, no en __uint128_t p. De acuerdo a documentos gcc no hay forma de declarar una constante de 128 bits:

GCC no admite la expresión de una constante entera de tipo __int128 para destinos con enteros largos de menos de 128 bits de ancho.

Entonces, parece que si bien puedes tener 128 bits Variablesno puedes tener 128 bits constantesa menos que su long long es de 128 bits de ancho.

La solución podría ser construir un valor de 128 bits a partir de constantes integrales “más estrechas” usando operaciones aritméticas básicas, y esperar que el compilador funcione plegado constante.

  • Tal vez “no puede tener constantes enteras de 128 bits, a menos que su intmax_t tiene al menos 128 bits de ancho”?

    – chux – Reincorporar a Monica

    16 de julio de 2015 a las 18:07


  • No estoy seguro. Los documentos citados mencionan específicamente el tipo “entero largo largo”.

    – el.pescado – нет войне

    16 de julio de 2015 a las 18:14

  • En el lector del proyecto de especificación C11, está claro que constantes enteras debe tener al menos el rango de long long/unsigned long long. no está claro si constantes enteras debe tener al menos el rango de intmax_t/uintmax_t. me gustaría pensar sería requerido.

    – chux – Reincorporar a Monica

    16 de julio de 2015 a las 18:32

  • @chux, estos tipos son extensiones y no están cubiertos por el estándar. Para el intmax_t tipos el estándar proporciona las macros correspondientes INTMAX_C que están garantizados para crear constantes del tipo correcto.

    – Jens Gusted

    16 de julio de 2015 a las 18:48

  • @el.pescado, si bien no hay soporte directo, aún puede tener expresiones constantes de ese tipo. P.ej ((__int128_t)1000000000000*HIGH)+LOW podría ser una forma de construir tal expresión para un valor grande, donde HIGH y LOW son los dígitos superior e inferior del número.

    – Jens Gusted

    16 de julio de 2015 a las 18:51


avatar de usuario
jerry73204

¿Has probado esto?

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

EDITAR 25 de noviembre

Perdón por la mala aclaración en la publicación anterior. En serio, no publiqué esta respuesta como una broma. Aunque el documento de GCC establece que no hay forma de expresar una constante entera de 128 bits, esta publicación simplemente proporciona una solución alterna para aquellos que quieren asignar valores a las variables __uint128_t con facilidad.

Puede intentar compilar el siguiente código con GCC (7.2.0) o Clang (5.0.0). Imprime los resultados deseados.

#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;
}

La salida estándar:

HIGH 0f0e0d0c0b0a0908
LOW  0706050403020100

Esto solo se considera como un solución alterna ya que juega trucos con los punteros al colocar el “valor” en la sección .rodata (si lo objdump), y no es portátil (x86_64 y aarch64 están bien pero no arm y x86). Creo que ha sido suficiente para quienes codifican en máquinas de escritorio.

  • ¿Esto pretende ser una broma?

    – Carreras de ligereza en órbita

    25/10/2015 a las 15:29

  • Incluso suponiendo que el valor de la cadena sea correcto para cualquier endianness que se necesite, eso probablemente resultará en un SIGBUS para cualquier arquitectura que tenga restricciones de alineación en __int128 variables

    –Andrew Henle

    25/10/2015 a las 15:33


  • Descubrí que en realidad funciona para gcc 5.2.0 en x86_64. Incluso con int n = *(int*) "1234";. Tal vez Andrés tenga razón. No se garantiza que la constante de cadena esté alineada y, por lo tanto, es posible que este truco no funcione en arcos que no sean x86.

    – jerry73204

    27 de octubre de 2015 a las 7:05


  • @jerry73204: en mi caso, el compilador se queja en C99 sobre el puntero con 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"})); ^

    – usuario2284570

    15 de junio de 2020 a las 23:58


  • Además de la alineación, la solución en esta respuesta también tiene problemas con el alias estricto, pero algo así como __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; obras. Asume el endianness y el ancho de char como 8 bits, pero también lo hace la respuesta anterior a este comentario.

    – Pascal Cuoq

    19 oct 2020 a las 11:20


Tuve el mismo problema y preparé una solución usando literales definidos por el usuario. Así es como crea una instancia del literal _xxl definido por el usuario:

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;
}

Producción:

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

Aquí está la implementación para el literal definido por el usuario _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__

Se puede usar en constexpr como constantes enteras normales:

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" );

  • Gracias por la respuesta y por su tiempo, pero su código es C++, no C.

    – azul

    27 de diciembre de 2019 a las 9:25

  • ups… no me di cuenta de eso

    – Fabio Fernández

    28 de diciembre de 2019 a las 11:25

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad