The GCC 4.7.1 manual says:
6.8 128-bits integers
As an extension the integer scalar type __int128
is supported for targets having an integer
mode wide enough to hold 128-bit. Simply write __int128
for a signed 128-bit integer, or
unsigned __int128
for an unsigned 128-bit integer. There is no support in GCC to express
an integer constant of type __int128
for targets having long long
integer with less then [sic]
128 bit width.
Interestingly, although that does not mention __uint128_t
, that type is accepted, even with stringent warnings set:
#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]
$
(This is with a home-compiled GCC 4.7.1 on Mac OS X 10.7.4.)
Change the constant to 0x12345678900987654321
and the compiler says:
xxx.c: In function ‘main’:
xxx.c:6:24: warning: integer constant is too large for its type [enabled by default]
So, it isn't easy manipulating these creatures. The outputs with the decimal constant and hex constants are:
ab54a98cdc6770b1
5678900987654321
For printing in decimal, your best bet is to see if the value is larger than UINT64_MAX; if it is, then you divide by the largest power of 10 that is smaller than UINT64_MAX, print that number (and you might need to repeat the process a second time), then print the residue modulo the largest power of 10 that is smaller than UINT64_MAX, remembering to pad with leading zeroes.
This leads to something like:
#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);
}
The output from that is:
24197857200151252746022455506638221840
38 digits
321944928255972408260334335944939549199
39 digits
We can verify using 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
$
Clearly, for hex, the process is simpler; you can shift and mask and print in just two operations. For octal, since 64 is not a multiple of 3, you have to go through analogous steps to the decimal operation.
The print_u128_u()
interface is not ideal, but it does at least return the number of characters printed, just as printf()
does. Adapting the code to format the result into a string buffer is a not wholly trivial exercise in programming, but not dreadfully difficult.
char str[40] = {0};
filled the whole array with zero already. – Oleasteruint64_t
– Zoriluint128_t
defined? Normally<inttypes.h>
should define bothuintN_t
andPRIuN
. (Mine only goes up to 64.) – Bichromate__uint128_t
is available ingcc
. It is not defined in<inttypes.h>
. – Bernardo__uint128_t
. But my question was specifically whereuint128_t
is defined, not__uint128_t
. If<inttypes.h>
definesuint128_t
, it should also definePRIu128
. – Bichromatetypedef __uint128_t uint128_t
. – Bernardo__uint128_t
depends on whether it was built with 64-bit support; I have gcc-4.7, but it's 32-bit only and doesn't have__uint128_t
. – Bichromate%.13
– Zoriluint128_t
numbers. – Bernardoprint(1000000000000000000LL);
– Zoril340282366920938463463374607431768211456
of them. ;) I've fixed the bug. – Bernardo__uint128_t
. It caused us problems on a number of platforms, like ARM64, ARMEL and S/390. We had to give up using it because it was so buggy. For example, GCC calculated the result ofu = 93 - 0 - 0 - 0
(using the 128-bit types) as18446744073709551615
on ARM64. – Biddickprintf("%w128d", my_128bit_integer);
thephd.dev/c-the-improvements-june-september-virtual-c-meeting – Bernardo