comment imprimer le numéro uint128 t en utilisant gcc?

Est-il PRIu128 qui se comporte de façon similaire à PRIu64 à partir de <inttypes.h> :

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

ou conversion manuelle de chiffres Par chiffres:

int print_uint128(uint128_t n) {
  if (n == 0)  return printf("0n");

  char str[40] = {0}; // log10(1 << 128) + '"151910920"'
  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("%sn", s);
}

est la seule option?

notez que uint128_t est mon propre typedef pour __uint128_t .

44
c gcc
demandé sur Keith Thompson 0000-00-00 00:00:00

11 réponses

Non il n'y a pas de support dans la bibliothèque pour imprimer 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 de l'arrière est une bonne, mais vous pourriez utiliser des morceaux beaucoup plus grands. 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 une uint64_t .

comme décimal, ces grands les nombres deviennent illisibles très vite, donc une autre option, plus facile, est de les imprimer en hex. Alors 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 ensuite essentiellement la même avec

  uint64_t high = (x >> 64);

pour la moitié supérieure.

14
répondu Jens Gustedt 2016-08-09 15:09:13

Le GCC 4.7.1 manuel dit:

6.8 entiers de 128 bits

comme extension le type scalaire entier __int128 est supporté pour les cibles ayant un entier mode assez large pour contenir 128 bits. Il suffit d'écrire __int128 pour un entier de 128 bits signé, ou unsigned __int128 pour un entier non signé de 128 bits. Il n'y a pas de soutien dans le GCC pour exprimer une constante entière de type __int128 pour cibles ayant un nombre entier long long inférieur à [ sic ] Largeur 128 bits.

fait intéressant, bien que cela ne mentionne pas __uint128_t , ce type est accepté, même avec des avertissements stricts mis:

#include <stdio.h>

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

de la 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 un GCC 4.7.1 compilé à la maison sur Mac OS X 10.7.4.)

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

ab54a98cdc6770b1
5678900987654321

pour imprimer en décimal, votre meilleur pari est de voir si la valeur est plus grande que UINT64_MAX; si elle est, alors vous divisez par la plus grande puissance de 10 qui est plus petite que UINT64_MAX, imprimer ce nombre (et vous pourriez avoir besoin de répéter le processus une deuxième fois), puis imprimer le résidu modulo la plus grande puissance de 10 qui est plus petite que UINT64_MAX, en se rappelant de pad avec des zéros de 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);
}

la sortie de celui 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 changer et masquer et imprimer en seulement deux opérations. Pour octal, puisque 64 n'est pas un multiple de 3, vous devez passer par 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, tout comme printf() . Adapter le code pour formater le résultat dans un tampon de chaîne est un exercice pas tout à fait trivial dans la programmation, mais pas terriblement difficile.

28
répondu Jonathan Leffler 2012-07-26 16:33:21

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

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 de 128 bits n'est pas aussi cher 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.)

5
répondu ephemient 2012-07-26 06:52:49

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);
}
3
répondu abelenky 2013-06-21 14:02:04

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);
3
répondu Tiago Sippert 2013-06-21 14:27:12

basé sur la réponse de sebastian, ceci est pour signed int128 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) + '"151900920"'
    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;
} 
3
répondu mosh 2017-07-04 15:46:40

en me basant sur la réponse d'abelenky ci-dessus, j'ai trouvé ceci.

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

, Qui semble fonctionner comme prévu.

1
répondu Perkins 2014-03-15 04:18:47

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 ce qui suit:

print_uint128_decimal(-1); // Assuming -1's complement being 0xFFFFF...
0
répondu bumfo 2017-01-16 06:14:57

variante C++. Vous pouvez l'utiliser comme un modèle pour dériver spécialisé version C 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');
    }
}
0
répondu Orient 2018-02-01 10:16:50

comment imprimer le numéro __uint128_t en utilisant gcc?

Y at-il PRIu128 qui se comporte semblable à PRIu64 de:

Pas de. Au lieu d'imprimer dans décimal , imprimer sur une chaîne.

la taille du tampon de chaîne nécessaire est juste suffisante pour faire le travail par la valeur 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) = '"151900920"';
  } else {
    *uint128toa_helper(dest, (uint128_t) x) = '"151900920"';
  }
  return dest;
}

char *uint128toa(char *dest, uint128_t x) {
  *uint128toa_helper(dest, x) = '"151900920"';
  return dest;
}

Test. Taille du 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
0
répondu chux 2018-02-25 05:44:26

un peu comme #3

unsigned __int128 g = ...........;

printf ("g = 0x%lx%lx\r\n", (uint64_t) (g >> 64), (uint64_t) g);
-2
répondu enzo 2017-12-13 15:35:43