come stampare il numero __uint128_t usando gcc?

C’è PRIu128 che si comporta in modo simile a PRIu64 da :

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

O convertire manualmente cifra per cifra:

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

è l’unica opzione?

Nota che uint128_t è il mio typedef per __uint128_t .

No, non c’è supporto nella libreria per la stampa di questi tipi. Non sono nemmeno tipi interi estesi nel senso dello standard C.

La tua idea di iniziare la stampa dal retro è buona, ma potresti usare blocchi molto più grandi. In alcuni test per P99 ho una funzione simile che utilizza

 uint64_t const d19 = UINT64_C(10000000000000000000); 

come la più grande potenza di 10 che si adatta a un uint64_t .

Essendo decimali, questi grandi numeri diventano illeggibili molto presto quindi un’altra opzione, più semplice, è stamparli in esadecimale. Quindi puoi fare qualcosa di simile

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

per ottenere la metà inferiore e quindi fondamentalmente la stessa cosa con

  uint64_t high = (x >> 64); 

per la metà superiore.

Il manuale GCC 4.7.1 dice:

6.8 numeri interi a 128 bit

Come estensione, il tipo scalare intero __int128 è supportato per i target aventi una modalità intera sufficientemente ampia da contenere 128 bit. È sufficiente scrivere __int128 per un intero con unsigned __int128 128 bit o unsigned __int128 per un numero intero a 128 bit senza segno. Non c’è alcun supporto in GCC per esprimere una costante intera di tipo __int128 per i target con long long intero long long con meno di [ sic ] 128 bit di larghezza.

È interessante notare che, sebbene ciò non menzioni __uint128_t , quel tipo è accettato, anche con set di avvertenze stringenti:

 #include  int main(void) { __uint128_t u128 = 12345678900987654321; printf("%llx\n", (unsigned long long)(u128 & 0xFFFFFFFFFFFFFFFF)); return(0); } 

Compilazione:

 $ 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] $ 

(Questo è con GCC 4.7.1 compilato in home su Mac OS X 10.7.4.)

Cambia la costante in 0x12345678900987654321 e il compilatore dice:

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

Quindi, non è facile manipolare queste creature. Le uscite con la costante decimale e le costanti esadecimali sono:

 ab54a98cdc6770b1 5678900987654321 

Per la stampa in decimale, la soluzione migliore è vedere se il valore è maggiore di UINT64_MAX; se lo è, allora dividi per la potenza massima di 10 che è più piccola di UINT64_MAX, stampa quel numero (e potresti dover ripetere la procedura una seconda volta), quindi stampa il modulo residuo la potenza maggiore di 10 che è minore di UINT64_MAX, ricordando di eseguire il pad con gli zeri iniziali.

Questo porta a qualcosa di simile:

 #include  #include  /* ** 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); } 

L’output da quello è:

 24197857200151252746022455506638221840 38 digits 321944928255972408260334335944939549199 39 digits 

Possiamo verificare usando 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 $ 

Chiaramente, per hex, il processo è più semplice; puoi spostare e mascherare e stampare in sole due operazioni. Per ottale, dal 64 non è un multiplo di 3, devi passare attraverso passaggi analoghi all’operazione decimale.

L’interfaccia print_u128_u() non è l’ideale, ma restituisce almeno il numero di caratteri stampati, proprio come fa printf() . Adattare il codice per formattare il risultato in un buffer di stringa è un esercizio non banale nella programmazione, ma non terribilmente difficile.

Non ho una soluzione integrata, ma la divisione / modulo è costosa. È ansible convertire il binario in decimale solo con turni.

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

(Ma apparentemente la divisione / modulo a 128 bit non è costosa come pensavo: su un Phenom 9600 con GCC 4.7 e Clang 3.1 a -O2 , sembra che sia eseguito un 2x-3x più lento del metodo OP.)

Penso che la tua funzione print_uint128 sia terribilmente complessa.

Non è più semplice scrivere e correre?

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

Puoi usare questa semplice 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); 

In base alla risposta di sebastian, questo è per int128 firmato in g ++, non thread-safe.

 // g++ -Wall fact128.c && a.exe // 35! overflows 128bits #include  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; } 

Risolvendo la risposta di abelenky sopra, mi sono inventato questo.

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

Che sembra funzionare come previsto.

Ecco una versione modificata della risposta di Leffler che supporta da 0 a 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; } 

E prova questo:

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

Variante C ++. Puoi usarlo come modello per ottenere versioni C specializzate della funzione:

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

come stampare il numero __uint128_t usando gcc?
C’è PRIu128 che si comporta in modo simile a PRIu64 da:

No. Invece di stampare in decimale , stampare su una stringa.

La dimensione del buffer di stringa necessario è appena sufficiente per eseguire il lavoro in base al valore di 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; } 

Test. Dimensione del buffer del caso peggiore: 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; } 

Produzione

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

molto simile a # 3

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