c gcc

cómo imprimir el número__uint128_t usando gcc?



(11)

¿Hay PRIu128 que se comporte de manera similar a PRIu64 desde <inttypes.h> :

printf("%" PRIu64 "/n", some_uint64_value);

O convirtiendo manualmente dígito por dígito:

int print_uint128(uint128_t n) { if (n == 0) return printf("0/n"); char str[40] = {0}; // log10(1 << 128) + ''/0'' char *s = str + sizeof(str) - 1; // start at the end while (n != 0) { if (s == str) return -1; // never happens *--s = "0123456789"[n % 10]; // save last digit n /= 10; // drop it } return printf("%s/n", s); }

es la única opción?

Tenga en cuenta que uint128_t es mi propio typedef para __uint128_t .


cómo imprimir el número __uint128_t usando gcc?
¿Hay PRIu128 que se comporte de manera similar a PRIu64 desde:

No. En lugar de imprimir en decimal , imprima en una cadena.

El tamaño del buffer de cadena necesario es suficiente para hacer el trabajo por el valor de x .

typedef signed __int128 int128_t; typedef unsigned __int128 uint128_t; // Return pointer to the end static char *uint128toa_helper(char *dest, uint128_t x) { if (x >= 10) { dest = uint128toa_helper(dest, x / 10); } *dest = (char) (x % 10 + ''0''); return ++dest; } char *int128toa(char *dest, int128_t x) { if (x < 0) { *dest = ''-''; *uint128toa_helper(dest + 1, (uint128_t) (-1 - x) + 1) = ''/0''; } else { *uint128toa_helper(dest, (uint128_t) x) = ''/0''; } return dest; } char *uint128toa(char *dest, uint128_t x) { *uint128toa_helper(dest, x) = ''/0''; return dest; }

Prueba. El peor tamaño de búfer de casos: 41.

int main(void) { char buf[41]; puts("1234567890123456789012345678901234567890"); puts(uint128toa(buf, 0)); puts(uint128toa(buf, 1)); puts(uint128toa(buf, (uint128_t) -1)); int128_t mx = ((uint128_t) -1) / 2; puts(int128toa(buf, -mx - 1)); puts(int128toa(buf, -mx)); puts(int128toa(buf, -1)); puts(int128toa(buf, 0)); puts(int128toa(buf, 1)); puts(int128toa(buf, mx)); return 0; }

Salida

1234567890123456789012345678901234567890 0 1 340282366920938463463374607431768211455 -170141183460469231731687303715884105728 -170141183460469231731687303715884105727 -1 0 1 170141183460469231731687303715884105727


Aquí hay una versión modificada de la respuesta de Leffler que admite desde 0 hasta UINT128_MAX

/* UINT64_MAX 18446744073709551615ULL */ #define P10_UINT64 10000000000000000000ULL /* 19 zeroes */ #define E10_UINT64 19 #define STRINGIZER(x) # x #define TO_STRING(x) STRINGIZER(x) int print_uint128_decimal(__uint128_t big) { size_t rc = 0; size_t i = 0; if (big >> 64) { char buf[40]; while (big / P10_UINT64) { rc += sprintf(buf + E10_UINT64 * i, "%." TO_STRING(E10_UINT64) PRIu64, (uint64_t)(big % P10_UINT64)); ++i; big /= P10_UINT64; } rc += printf("%" PRIu64, (uint64_t)big); while (i--) { fwrite(buf + E10_UINT64 * i, sizeof(char), E10_UINT64, stdout); } } else { rc += printf("%" PRIu64, (uint64_t)big); } return rc; }

Y prueba esto:

print_uint128_decimal(-1); // Assuming -1''s complement being 0xFFFFF...


Creo que su función print_uint128 es tremendamente compleja.

¿No es más fácil de escribir y ejecutar?

void print_uint128(uint128_t n) { if (n == 0) { return; } print_uint128(n/10); putchar(n%10+0x30); }


El manual de GCC 4.7.1 dice:

6.8 enteros de 128 bits

Como extensión, el tipo escalar entero __int128 es compatible con los objetivos que tienen un modo entero lo suficientemente ancho como para contener 128 bits. Simplemente escriba __int128 para un entero firmado de 128 bits, o unsigned __int128 para un entero unsigned __int128 signo de 128 bits. No hay soporte en GCC para expresar una constante entera de tipo __int128 para objetivos que tienen long long entero long long con menos de [ sic ] ancho de 128 bits.

Curiosamente, aunque eso no menciona __uint128_t , ese tipo es aceptado, incluso con estrictas advertencias establecidas:

#include <stdio.h> int main(void) { __uint128_t u128 = 12345678900987654321; printf("%llx/n", (unsigned long long)(u128 & 0xFFFFFFFFFFFFFFFF)); return(0); }

Compilacion:

$ gcc -O3 -g -std=c99 -Wall -Wextra -pedantic xxx.c -o xxx xxx.c: In function ‘main’: xxx.c:6:24: warning: integer constant is so large that it is unsigned [enabled by default] $

(Esto es con un GCC 4.7.1 compilado en el hogar en Mac OS X 10.7.4.)

Cambie la constante a 0x12345678900987654321 y el compilador dice:

xxx.c: In function ‘main’: xxx.c:6:24: warning: integer constant is too large for its type [enabled by default]

Por lo tanto, no es fácil manipular estas criaturas. Las salidas con la constante decimal y las constantes hexadecimales son:

ab54a98cdc6770b1 5678900987654321

Para imprimir en decimal, su mejor apuesta es ver si el valor es mayor que UINT64_MAX; si es así, se divide por la mayor potencia de 10 que es más pequeña que UINT64_MAX, imprima ese número (y es posible que deba repetir el proceso por segunda vez), luego imprima el módulo de residuos con la potencia más grande de 10 que es más pequeña que UINT64_MAX, recordando rellenar con ceros a la izquierda.

Esto lleva a algo así como:

#include <stdio.h> #include <inttypes.h> /* ** Using documented GCC type unsigned __int128 instead of undocumented ** obsolescent typedef name __uint128_t. Works with GCC 4.7.1 but not ** GCC 4.1.2 (but __uint128_t works with GCC 4.1.2) on Mac OS X 10.7.4. */ typedef unsigned __int128 uint128_t; /* UINT64_MAX 18446744073709551615ULL */ #define P10_UINT64 10000000000000000000ULL /* 19 zeroes */ #define E10_UINT64 19 #define STRINGIZER(x) # x #define TO_STRING(x) STRINGIZER(x) static int print_u128_u(uint128_t u128) { int rc; if (u128 > UINT64_MAX) { uint128_t leading = u128 / P10_UINT64; uint64_t trailing = u128 % P10_UINT64; rc = print_u128_u(leading); rc += printf("%." TO_STRING(E10_UINT64) PRIu64, trailing); } else { uint64_t u64 = u128; rc = printf("%" PRIu64, u64); } return rc; } int main(void) { uint128_t u128a = ((uint128_t)UINT64_MAX + 1) * 0x1234567890ABCDEFULL + 0xFEDCBA9876543210ULL; uint128_t u128b = ((uint128_t)UINT64_MAX + 1) * 0xF234567890ABCDEFULL + 0x1EDCBA987654320FULL; int ndigits = print_u128_u(u128a); printf("/n%d digits/n", ndigits); ndigits = print_u128_u(u128b); printf("/n%d digits/n", ndigits); return(0); }

El resultado de eso es:

24197857200151252746022455506638221840 38 digits 321944928255972408260334335944939549199 39 digits

Podemos verificar el uso de bc :

$ bc bc 1.06 Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc. This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty''. ibase = 16 1234567890ABCDEFFEDCBA9876543210 24197857200151252746022455506638221840 F234567890ABCDEF1EDCBA987654320F 321944928255972408260334335944939549199 quit $

Claramente, para hex, el proceso es más simple; puede cambiar y enmascarar e imprimir en solo dos operaciones. Para octal, ya que 64 no es un múltiplo de 3, debe pasar por pasos análogos a la operación decimal.

La interfaz print_u128_u() no es ideal, pero al menos devuelve la cantidad de caracteres impresos, al igual que printf() . Adaptar el código para formatear el resultado en un buffer de cadena es un ejercicio no trivial en la programación, pero no terriblemente difícil.


No tengo una solución integrada, pero la división / módulo es costosa. Puede convertir binario a decimal con solo cambios.

static char *qtoa(uint128_t n) { static char buf[40]; unsigned int i, j, m = 39; memset(buf, 0, 40); for (i = 128; i-- > 0;) { int carry = !!(n & ((uint128_t)1 << i)); for (j = 39; j-- > m + 1 || carry;) { int d = 2 * buf[j] + carry; carry = d > 9; buf[j] = carry ? d - 10 : d; } m = j; } for (i = 0; i < 38; i++) { if (buf[i]) { break; } } for (j = i; j < 39; j++) { buf[j] += ''0''; } return buf + i; }

(Pero aparentemente la división / módulo de 128 bits no es tan cara como pensaba. En un Phenom 9600 con GCC 4.7 y Clang 3.1 en -O2 , esto parece funcionar un 2x-3x más lento que el método de OP).


No, no hay soporte en la biblioteca para imprimir estos tipos. Ni siquiera son tipos enteros extendidos en el sentido del estándar C.

Su idea para iniciar la impresión desde la parte posterior es buena, pero podría usar trozos mucho más grandes. En algunas pruebas para P99 tengo tal función que usa

uint64_t const d19 = UINT64_C(10000000000000000000);

como la mayor potencia de 10 que cabe en uint64_t .

Como decimal, estas grandes cantidades son ilegibles muy pronto así que otra opción más fácil es imprimirlas en hexadecimal. Entonces puedes hacer algo como

uint64_t low = (uint64_t)x; // This is UINT64_MAX, the largest number in 64 bit // so the longest string that the lower half can occupy char buf[] = { "18446744073709551615" }; sprintf(buf, "%" PRIX64, low);

para obtener la mitad inferior y luego básicamente lo mismo con

uint64_t high = (x >> 64);

para la mitad superior.


Puedes usar esta simple macro:

typedef __int128_t int128 ; typedef __uint128_t uint128 ; uint128 x = (uint128) 123; printf("__int128 max %016"PRIx64"%016"PRIx64"/n",(uint64)(x>>64),(uint64)x);


Según la respuesta de Sebastián, esto se debe a la firma int128 en g ++, no a prueba de hilos.

// g++ -Wall fact128.c && a.exe // 35! overflows 128bits #include <stdio.h> char * sprintf_int128( __int128_t n ) { static char str[41] = { 0 }; // sign + log10(2**128) + ''/0'' char *s = str + sizeof( str ) - 1; // start at the end bool neg = n < 0; if( neg ) n = -n; do { *--s = "0123456789"[n % 10]; // save last digit n /= 10; // drop it } while ( n ); if( neg ) *--s = ''-''; return s; } __int128_t factorial( __int128_t i ) { return i < 2 ? i : i * factorial( i - 1 ); } int main( ) { for( int i = 0; i < 35; i++ ) printf( "fact(%d)=%s/n", i, sprintf_int128( factorial( i ) ) ); return 0; }


Trabajando con la respuesta de Abelenky arriba, se me ocurrió esto.

void uint128_to_str_iter(uint128_t n, char *out,int firstiter){ static int offset=0; if (firstiter){ offset=0; } if (n == 0) { return; } uint128_to_str_iter(n/10,out,0); out[offset++]=n%10+0x30; } char* uint128_to_str(uint128_t n){ char *out=calloc(sizeof(char),40); uint128_to_str_iter(n, out, 1); return out; }

Lo cual parece funcionar como se esperaba.


Variante C ++ Puede usarlo como plantilla para obtener una versión C especializada de la función:

template< typename I > void print_uint(I value) { static_assert(std::is_unsigned< I >::value, "!"); if (value == 0) { putchar_unlocked(''0''); return; } I rev = value; I count = 0; while ((rev % 10) == 0) { ++count; rev /= 10; } rev = 0; while (value != 0) { rev = (rev * 10) + (value % 10); value /= 10; } while (rev != 0) { putchar_unlocked(''0'' + (rev % 10)); rev /= 10; } while (0 != count) { --count; putchar_unlocked(''0''); } }


muy parecido a # 3

unsigned __int128 g = ...........; printf ("g = 0x%lx%lx/r/n", (uint64_t) (g >> 64), (uint64_t) g);