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.