Is there PRIu128
that behaves similar to PRIu64
from <inttypes.h>
:
printf("%" PRIu64 "\n", some_uint64_value);
Or converting manually digit by digit:
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); }
is the only option?
Note that uint128_t
is my own typedef for __uint128_t
.
Since the __uint128_t type is a GCC extension, the proper thing to do is probably to check for some known-good version of GCC. See this page for information about the macros used to version-check the GCC compiler. Follow this answer to receive notifications.
The int128 type defines a signed 128-bit integer. The API is meant to mimic an intrinsic integer type as closely as possible, so that any forthcoming int128_t can be a drop-in replacement. The int128 type supports the following: Implicit conversion from signed integral types and unsigned types narrower than 128 bits.
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, orunsigned __int128
for an unsigned 128-bit integer. There is no support in GCC to express an integer constant of type__int128
for targets havinglong 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With