Reputation: 414745
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
.
Upvotes: 68
Views: 87605
Reputation: 154169
how to print __uint128_t number using gcc?
Is there PRIu128 that behaves similar to PRIu64 from <inttypes.h>:
No. Instead to print in decimal, print to a string.
The size of string buffer needed is just enough to do the job per the value of 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) = '\0';
} else {
*uint128toa_helper(dest, (uint128_t) x) = '\0';
}
return dest;
}
char *uint128toa(char *dest, uint128_t x) {
*uint128toa_helper(dest, x) = '\0';
return dest;
}
Test. Worst case buffer size: 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;
}
Output
1234567890123456789012345678901234567890
0
1
340282366920938463463374607431768211455
-170141183460469231731687303715884105728
-170141183460469231731687303715884105727
-1
0
1
170141183460469231731687303715884105727
[2025 update]
For a non-recursive approach, see end of
review.
Upvotes: 3
Reputation: 80
Easiest way to print unsigned int 128 bit
or int 128 bit
:
To print __int128
, you must use __int128_t
data type. Because there is no data type named __int128. At least I didn't find any till now.
And You must use "typecasting" every time in assigning value and outputting values.
you must use format specifier %lld
for __int128_t
and %llu
for __uint128_t
.
in C++, std::cout
printing...
you must use static_cast <long long> (var_name)
Code:
#include <iostream>
int main()
{
// int 128 bit
__int128_t var = (long long)-23542342345435;
// unsigned int 128 bit
__uint128_t var2 = (long long)64324234234234;
// printing in c++
std::cout << "int 128 bit: "
<< static_cast<long long>(var) << std::endl;
// printing in c
printf("int 128 bit: %lld\n", (long long)var);
// printing in c++
std::cout << "unsinged int 128 bit: "
<< static_cast<long long>(var2) << std::endl;
// printing in c
printf("unsinged int 128 bit: %llu\n", (long long)var2);
return 0;
}
Output:
PS D:\CodeFiles\Master-of-C-programming\Day2>
g++ v7.cpp -o v7
.\v7
int 128 bit: -23542342345435
int 128 bit: -23542342345435
unsinged int 128 bit: 64324234234234
unsinged int 128 bit: 64324234234234
previously commented on this: https://stackoverflow.com/a/78573447/6546572
Upvotes: 0
Reputation: 11
When a number contains too many digits, it's going to be too hard to read. In order to print number in thousands with locale sensitive format for 128-bit integers, we have to divide a 128-bit integer into smaller parts with number of digits in multiples of 3. A 128-bit integer has maximum 39 digits. We can divide it into 3 parts with maximum 15 or 18 digits for each part. This way, we can print 128-bit integers in the format like:
1,234,567,890,000,000,123,456 (in en_US locale)
1.234.567.890.000.000.123.456 (in de_DE locale)
1 234 567 890 000 000 123 456 (in en_CA locale)
For portability, the following code simply uses C's sprintf, because C++ locale does not work in gcc stdlib with Windows platform.
#if (LONG_INT_BITS==32)
#define long_int_suffix "ll" // Windows long is 32 bit
#else
#define long_int_suffix "l" // Linux and the rest uses 64 bit
#endif
// assuming buffer is large enough for a 128 integer
// when grouping is true, numbers will be grouped in thousands by a separator
// of the current locale (by calling setlocale(LC_NUMERIC, locale_code);
size_t print_uint128(char* buffer, __uint128_t val, bool grouping)
{
// separate 128-bit integer into three parts: (high2, high1, low), each part may have maximum 18 digits
const uint64_t divisor = 1000000000000000000ULL;
__uint128_t high = val / divisor;
uint64_t high2 = high / divisor;
uint64_t high1 = high % divisor;
uint64_t low = val % divisor;
size_t length_printed = 0;
size_t section_length;
if (high2 > 0)
{
// print the first, the most significant part
length_printed = sprintf(buffer, grouping ? "%'" long_int_suffix "u" : "%" long_int_suffix "u", high2);
buffer += length_printed ;
}
if (high2 > 0 || high1 > 0)
{
// the second part, even when zero, needs to be printed if the first part has been printed
if (high2 > 0)
{
if (grouping)
{
// if grouping is required, add divisor in order to get proper thousand seperator inserted
// in leading zeros
high1 += divisor;
section_length = sprintf(buffer, "%'" long_int_suffix "u", high1);
}
else
{
section_length = sprintf(buffer, "%012" long_int_suffix "u", high1);
}
}
else
{
section_length = sprintf(buffer, grouping ? "%'" long_int_suffix "u" : "%" long_int_suffix "u", high1);
}
if (grouping && high2 > 0)
{
// move all digits in this part backward by one to remove the extra leading '1' printed due to the
// addition of the value divisor
memcpy(buffer, buffer+1, section_length);
--section_length;
}
buffer += section_length;
length_printed += section_length;
}
if (high2 > 0 || high1 > 0)
{
if (grouping)
{
// add divisor in order to get proper thousand seperator inserted in leading zeros
low += divisor;
section_length = sprintf(buffer, "%'" long_int_suffix "u", low);
// remove the extra leading '1'
memcpy(buffer, buffer+1, section_length);
--section_length;
}
else
{
section_length = sprintf(buffer, "%012" long_int_suffix "u", low);
}
}
else
{
section_length = sprintf(buffer, grouping ? "%'" long_int_suffix "u" : "%" long_int_suffix "u", low);
}
return length_printed += section_length ;
}
size_t print_int128(char* buffer, __int128_t val, bool grouping, bool print_positive_sign)
{
__uint128_t uval;
size_t sign_byte =0;
if (val < 0)
{
uval = -val;
*buffer++ = '-';
sign_byte = 1;
}
else
{
uval = val;
if (print_positive_sign)
{
*buffer++ = '+';
sign_byte = 1;
}
}
return print_uint128(buffer, uval, grouping) + sign_byte;
}
Upvotes: 0
Reputation: 9
You can redefine operators cin and cout for work with __int128_t. You should only convert __int128_t to strings and cin/cout strings
typedef __int128_t lint;
istream& operator >> (istream &in, lint &x) {
string s;
in >> s;
for (lint i = s.size() - 1, p = 1; i >= 0; i--, p *= 10) x += p * (s[i] - '0');
return in;
}
ostream& operator << (ostream &out, lint x) {
string s;
while (x > 0) {
s.push_back(x % 10 + '0');
x /= 10;
}
reverse(s.begin(), s.end());
out << s;
return out;
}
Upvotes: -1
Reputation: 301
In my previous answer I showed how I did print 128bit numbers based on "printf()".
I have implemented a 256bit unsigned integer type uint256_t as:
typedef __uint128_t uint256_t[2];
I have implemented the operations needed, some like "sqr()" taking an __uint128_t as argument and computing uint256_t as result.
I had hexadecimal print for uint256_t, and now wanted decimal print. But currently my uint256_t has only "mod_256()", but no "div()", so "n/=10" seen in many answers was no option. I found a (slow) solution that works, and since I use prints outside timed secions only, this is acceptable. Code can be found in this gist (including compile command details):
https://gist.github.com/Hermann-SW/83c8ab9e10a0bb64d770af543ed08445
In case you run sqr.cpp with an arg, it just outputs UINT256_MAX and exits:
if (argc>1) { pu256(UINT256_MAX); puts(""); return 0; }
$ ./sqr 1
115792089237316195423570985008687907853269984665640564039457584007913129639935
$
The tricky part was the recursive call to go up to maximal used digit, and subtract 1st digit and output that. Recursion does the rest. Function "pu256()" used fast multiplication by 10 "mul10()":
...
void mul10(uint256_t d, uint256_t x)
{
uint256_t t = { x[0], x[1] };
shl_256(t, 2);
add_256(d, x, t);
shl_256(d, 1);
}
const uint256_t UINT256_MAX_10th = UINT256( UINT128(0x1999999999999999, 0x9999999999999999), UINT128(0x9999999999999999, 0x999999999999999A) );
void pu256_(uint256_t v, uint256_t t, const uint256_t o)
{
if (!lt_256(v, t) && le_256(o, UINT256_MAX_10th))
{
uint256_t nt, no = { t[0], t[1] };
mul10(nt, t);
pu256_(v, nt, no);
}
char d = '0';
while (le_256(o, v))
{
sub_256(v, v, o);
++d;
}
putchar(d);
}
void pu256(const uint256_t u)
{
if ((u[1]==0) && (u[0]==0)) putchar('0');
else
{
uint256_t v = { u[0], u[1] }, t = UINT256( 0, 10 ), o = UINT256( 0, 1 );
pu256_(v, t, o);
}
}
...
As said, this approach only makes sense for integer type missing division operation.
Upvotes: 2
Reputation: 301
I wanted to print unsigned 64/128 bit numbers decimallly and did not want to reinvent the wheel. So "pu128()" has 3 cases: <10^19, <10^38, otherwise. Perhaps not the fastest, but should be portable. Defines UINT128_MAX as well as UINT128_C macros.
$ gcc -Wall -Wextra -pedantic lu.c
$ ./a.out
0
10000000000000000000
18446744073709551615
0
10000000000000000000
18446744073709551615
100000000000000000000000000000000000000
340282366920938463463374607431768211455
$
$ cat lu.c
#include <stdio.h>
#include <inttypes.h>
#define UINT128_C(u) ((__uint128_t)u)
void pu64(__uint64_t u) { printf("%" PRIu64, u); }
void pu640(__uint64_t u) { printf("%019" PRIu64, u); }
#define D19_ UINT64_C(10000000000000000000)
const __uint128_t d19_ = D19_;
const __uint128_t d38_ = UINT128_C(D19_)*D19_;
const __uint128_t UINT128_MAX = UINT128_C(UINT64_MAX)<<64 | UINT64_MAX;
void pu128(__uint128_t u)
{
if (u < d19_) pu64(u);
else if (u < d38_) { pu64(u/d19_); pu640(u%d19_); }
else { pu64(u/d38_); u%=d38_; pu640(u/d19_); pu640(u%d19_); }
}
int main()
{
pu64(0); puts("");
pu64(d19_); puts("");
pu64(UINT64_MAX); puts("");
pu128(0); puts("");
pu128(d19_); puts("");
pu128(UINT64_MAX); puts("");
pu128(d38_); puts("");
pu128(UINT128_MAX); puts("");
}
$
Upvotes: 3
Reputation: 631
This is for C++ but I'll leave it here since I haven't found a C++ version of this question for unsigned 128-bit ints.
Here's a simple, readable way to convert a uint128 to a base-10 string (which you can then print or do whatever you'd like with):
std::string toString(__uint128_t num) {
std::string str;
do {
int digit = num % 10;
str = std::to_string(digit) + str;
num = (num - digit) / 10;
} while (num != 0);
return str;
}
If needed, we can make it several times faster by getting the digits in larger chunks instead of one at a time. But it requires us to check each chunk for any leading zeroes that have been lost and add them back in:
std::string toString(__uint128_t num) {
auto tenPow19 = 10000000000000000000;
std::string str;
do {
uint64_t digits = num % tenPow19;
auto digitsStr = std::to_string(digits);
auto leading0s = (digits != num) ? std::string(19 - digitsStr.length(), '0') : "";
str = leading0s + digitsStr + str;
num = (num - digits) / tenPow19;
} while (num != 0);
return str;
}
Upvotes: -1
Reputation: 16731
C++ variant. You may use it as a template to derive specialized C-version of the function:
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');
}
}
Upvotes: 0
Reputation: 1
much like #3
unsigned __int128 g = ...........;
printf ("g = 0x%lx%lx\r\n", (uint64_t) (g >> 64), (uint64_t) g);
Upvotes: -2
Reputation: 1528
Based on sebastian's answer, this is for signed int128 in g++, not 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) + '\0'
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;
}
Upvotes: 5
Reputation: 252
Here's a modified version of Leffler's answer that supports from 0 to 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;
}
And try this:
print_uint128_decimal(-1); // Assuming -1's complement being 0xFFFFF...
Upvotes: 0
Reputation: 78963
No there isn't support in the library for printing these types. They aren't even extended integer types in the sense of the C standard.
Your idea for starting the printing from the back is a good one, but you could use much larger chunks. In some tests for P99 I have such a function that uses
uint64_t const d19 = UINT64_C(10000000000000000000);
as the largest power of 10 that fits into an uint64_t
.
As decimal, these big numbers get unreadable very soon so another, easier, option is to print them in hex. Then you can do something like
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);
to get the lower half and then basically the same with
uint64_t high = (x >> 64);
for the upper half.
Upvotes: 26
Reputation: 2517
Working off of abelenky's answer above, I came up with this.
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;
}
Which seems to work as intended.
Upvotes: 2
Reputation:
You can use this simple macro :
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);
Upvotes: 4
Reputation: 754820
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.
Upvotes: 43
Reputation: 204966
I don't have a built-in solution, but division/modulus is expensive. You can convert binary to decimal with just shifts.
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;
}
(But apparently 128-bit division/modulus are not as expensive as I thought. On a Phenom 9600 with GCC 4.7 and Clang 3.1 at -O2
, this seems to run a 2x-3x slower than OP's method.)
Upvotes: 10