64 votes

comment imprimer le numéro __uint128_t en utilisant gcc?

Existe-t-il PRIu128 comportement similaire à PRIu64 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 mon propre typedef pour __uint128_t .

38voto

Jonathan Leffler Points 299946

La GCC 4.7.1 manuel dit:

6.8 128-bits entiers

Comme une extension de l'entier de type scalaire __int128 est pris en charge pour les cibles ayant un nombre entier mode assez large pour contenir 128 bits. Simplement écrire __int128 signé 128 bits d'un entier ou d' unsigned __int128 pour un unsigned 128 bits entier. Il n'y a pas de support dans GCC pour exprimer une constante entière de type __int128 pour les cibles ayant long long entier, avec moins de [sic] De 128 bits de largeur.

Il est intéressant de noter, bien que cela ne fait pas mention d' __uint128_t, ce type est accepté, même avec de sévères mises en garde de définir:

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

(C'est avec une maison compilé par GCC 4.7.1 sur Mac OS X 10.7.4.)

Changement de la constante d' 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, il n'est pas facile de la manipulation de ces créatures. Les sorties avec la constante décimale et hexadécimale constantes sont:

ab54a98cdc6770b1
5678900987654321

Pour l'impression en décimal, votre meilleur pari est de voir si la valeur est plus grande que UINT64_MAX; si c'est le cas, vous divisez par la plus grande puissance de 10 qui est plus petit que UINT64_MAX, l'impression que le nombre (et vous pourriez avoir besoin de répéter l'opération une deuxième fois), puis imprimez le résidu modulo la plus grande puissance de 10 qui est plus petit que UINT64_MAX, se souvenant de remplir avec des zéros.

Cela mène à 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);
}

La sortie de ce qui est:

24197857200151252746022455506638221840
38 digits
321944928255972408260334335944939549199
39 digits

Nous pouvons le vérifier à l'aide 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
$

Clairement, pour l'hexagone, le processus est plus simple; vous pouvez déplacer et d'un masque et d'impression en seulement deux opérations. Pour octal, puisque 64 n'est pas un multiple de 3, vous devez passer par analogue étapes à la valeur décimale de l'opération.

L' print_u128_u() interface n'est pas l'idéal, mais elle permet au moins renvoyer le nombre de caractères imprimés, tout comme printf() n'. Adapter le code pour formater le résultat dans une chaîne de tampon est pas totalement trivial de l'exercice dans la programmation, mais pas terriblement difficile.

22voto

Jens Gustedt Points 40410

Non il n'y a pas de support dans la bibliothèque pour l'impression de ces types. Ils ne sont même pas étendu les types d'entiers dans le sens de la norme.

Votre idée de départ de l'impression à partir de l'arrière est une bonne idée, mais vous pouvez utiliser beaucoup plus gros morceaux. Dans certains tests pour P99, j'ai une fonction qui utilise

uint64_t const d19 = UINT64_C(10000000000000000000);

comme la plus grande puissance de 10 qui s'insère dans l' uint64_t.

Qu'en décimal, ces grands nombres deviennent illisibles très bientôt pour une autre, plus facile, l'option est de les imprimer au format hexadécimal. Ensuite, vous pouvez faire quelque chose comme

  uint64_t low = (uint64_t)x;
  char buf[] = { "18446744073709551615" };
  sprintf(buf, "%" PRIX64, low);

pour obtenir la moitié inférieure et en gros le même avec

  uint64_t high = (x >> 64);

pour la moitié supérieure.

8voto

ephemient Points 87003

Je n'ai pas de solution intégrée, mais la division / module coûte cher. Vous pouvez convertir le binaire en décimal avec juste des décalages.

 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 / le module 128 bits ne sont pas aussi chers que je le pensais. Sur un Phenom 9600 avec GCC 4.7 et Clang 3.1 à -O2 , cela semble fonctionner 2x-3x plus lentement que la méthode OP.)

3voto

Claudio Daffra Points 119

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

2voto

Perkins Points 616

En partant de la réponse d'Abelenky ci-dessus, j'ai trouvé 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X