Y at-il PRIu128
qui se comporte semblable à PRIu64
à partir de <inttypes.h>
:
printf("%" PRIu64 "\n", some_uint64_value);
Ou convertir manuellement chiffre par chiffre:
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);
}
est la seule option?
Notez que uint128_t
est ma propre typedef pour __uint128_t
.
Non, la bibliothèque n'accepte pas l'impression de ces types. Ils ne sont même pas des types entiers étendus au sens de la norme C.
Votre idée pour commencer l’impression à partir du dos est bonne, mais vous pourriez utiliser des morceaux beaucoup plus gros. Dans certains tests pour P99, j'ai une telle fonction qui utilise
uint64_t const d19 = UINT64_C(10000000000000000000);
comme la plus grande puissance de 10 qui s'inscrit dans un uint64_t
.
Décimales, ces gros nombres deviennent illisibles très rapidement. Une autre option, plus simple, consiste à les imprimer au format hexadécimal. Ensuite, vous pouvez faire quelque chose comme
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);
pour obtenir la moitié inférieure et puis fondamentalement la même chose avec
uint64_t high = (x >> 64);
pour la moitié supérieure.
Le manuel GCC 4.7.1 dit:
6.8 entiers 128 bits
En tant qu'extension, le type scalaire entier
__int128
est pris en charge pour les cibles ayant un entier mode suffisamment large pour contenir 128 bits. Écrivez simplement__int128
pour un entier signé de 128 bits, ouunsigned __int128
pour un entier non signé de 128 bits. Il n’existe aucun support dans GCC pour exprimer une constante entière de type__int128
pour les cibles ayantlong long
entier avec moins que [sic] Largeur de 128 bits.
Fait intéressant, bien que cela ne mentionne pas __uint128_t
, ce type est accepté, même avec des avertissements rigoureux:
#include <stdio.h>
int main(void)
{
__uint128_t u128 = 12345678900987654321;
printf("%llx\n", (unsigned long long)(u128 & 0xFFFFFFFFFFFFFFFF));
return(0);
}
Compilation:
$ 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]
$
(Ceci est avec un GCC 4.7.1 compilé à la maison sur Mac OS X 10.7.4.)
Changez la constante en 0x12345678900987654321
et le compilateur dit:
xxx.c: In function ‘main’:
xxx.c:6:24: warning: integer constant is too large for its type [enabled by default]
Donc, ce n'est pas facile de manipuler ces créatures. Les sorties avec la constante décimale et les constantes hexadécimales sont les suivantes:
ab54a98cdc6770b1
5678900987654321
Pour imprimer en décimal, votre meilleur choix est de voir si la valeur est supérieure à UINT64_MAX; si c'est le cas, divisez par la plus grande puissance de 10 inférieure à UINT64_MAX, imprimez ce nombre (et vous devrez peut-être répéter le processus une seconde fois), puis imprimez au résidu modulo la plus grande puissance de 10 inférieure à UINT64_MAX, en rappelant de compléter avec des zéros en tête.
Cela conduit à quelque chose comme:
#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);
}
Le résultat de ceci est:
24197857200151252746022455506638221840
38 digits
321944928255972408260334335944939549199
39 digits
Nous pouvons vérifier en utilisant 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
$
Clairement, pour hex, le processus est plus simple; vous pouvez déplacer, masquer et imprimer en deux opérations seulement. Pour octal, puisque 64 n’est pas un multiple de 3, vous devez suivre des étapes analogues à l’opération décimale.
L’interface print_u128_u()
n’est pas idéale, mais elle renvoie au moins le nombre de caractères imprimés, exactement comme le permet printf()
. L'adaptation du code pour formater le résultat dans une mémoire tampon de chaîne n'est pas un exercice de programmation totalement trivial, mais pas terriblement difficile.
Je n'ai pas de solution intégrée, mais la division/module coûte cher. Vous pouvez convertir des valeurs binaires en valeurs décimales avec des décalages seulement.
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;
}
(Mais apparemment, la division/module 128 bits ne sont pas aussi coûteux que je le pensais. Sur un Phenom 9600 avec GCC 4.7 et Clang 3.1 sous -O2
, cela semble fonctionner 2x plus lentement que la méthode de OP.)
Je pense que votre fonction print_uint128
est terriblement complexe.
N'est-ce pas plus simple à écrire et à exécuter?
void print_uint128(uint128_t n)
{
if (n == 0) {
return;
}
print_uint128(n/10);
putchar(n%10+0x30);
}
Basé sur la réponse de sebastian, c'est pour int128 signé en g ++, pas thread-safe.
// 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;
}
Vous pouvez utiliser cette macro simple:
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);
En partant de la réponse de abelenky ci-dessus, je suis arrivé à cela.
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;
}
Ce qui semble fonctionner comme prévu.
comment imprimer le numéro __uint128_t en utilisant gcc?
Existe-t-il un PRIu128 similaire à PRIu64 à partir de:
N ° Au lieu d'imprimer dans decimal , imprimer sur une chaîne.
La taille du tampon de chaîne nécessaire est juste suffisante pour effectuer le travail avec la valeur 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;
}
Tester. Taille de la mémoire tampon dans le pire des cas: 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;
}
Sortie
1234567890123456789012345678901234567890
0
1
340282366920938463463374607431768211455
-170141183460469231731687303715884105728
-170141183460469231731687303715884105727
-1
0
1
170141183460469231731687303715884105727
Variante C++. Vous pouvez l'utiliser comme modèle pour dériver la version C spécialisée de la fonction:
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');
}
}
Voici une version modifiée de la réponse de Leffler qui prend en charge de 0 à 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;
}
Et essayez ceci:
print_uint128_decimal(-1); // Assuming -1's complement being 0xFFFFF...