65

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • 2
    Rather than performing the print in the function, I'd return a string representation, so I could do things with it other than directly print it. – Wug Jul 25 '12 at 18:34
  • @DanielFischer: `char str[40] = {0};` filled the whole array with zero already. – kennytm Jul 25 '12 at 18:38
  • @Wug: yes. Normally I would. It is just an example to avoid the boiler-plate with passing buffers around. – jfs Jul 25 '12 at 18:38
  • @KennyTM Oh, duh! How did I overlook that? Thanks for the correction. – Daniel Fischer Jul 25 '12 at 18:40
  • you could print it with two (or three) `uint64_t` – Karoly Horvath Jul 25 '12 at 19:39
  • Where is `uint128_t` defined? Normally `` should define both `uintN_t` and `PRIuN`. (Mine only goes up to 64.) – Keith Thompson Jul 25 '12 at 19:58
  • @KeithThompson: `__uint128_t` is available in `gcc`. It is not defined in ``. – jfs Jul 25 '12 at 20:26
  • @KarolyHorvath: do you mean [something like this](https://gist.github.com/7f7e7451b60aff301fe0/6db4d64875caf1d3c7a0a5a5a66f3e96a8ccca16)? – jfs Jul 25 '12 at 20:43
  • @J.F.Sebastian: Perhaps it depends on which gcc you're using; mine doesn't have `__uint128_t`. But my question was specifically where `uint128_t` is defined, not `__uint128_t`. If `` defines `uint128_t`, it should also define `PRIu128`. – Keith Thompson Jul 25 '12 at 20:46
  • @KeithThompson: gcc version - 4.6.3. I've used `typedef __uint128_t uint128_t`. – jfs Jul 25 '12 at 21:02
  • Then you should mention that in the question. I think gcc support for `__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`. – Keith Thompson Jul 25 '12 at 21:09
  • @J.F. Sebastian: something like that, though for the lower "digits" you need `%.13` – Karoly Horvath Jul 25 '12 at 21:44
  • @Karoly Horvath: I don't understand. [It works](https://gist.github.com/7f7e7451b60aff301fe0/) for all `uint128_t` numbers. – jfs Jul 25 '12 at 21:55
  • @J.F. Sebastian: if it works for 4 numbers, it works for all of them? :) `print(1000000000000000000LL);` – Karoly Horvath Jul 25 '12 at 22:05
  • @KarolyHorvath: yes. [It should work](https://gist.github.com/7f7e7451b60aff301fe0) for *all* `340282366920938463463374607431768211456` of them. ;) I've fixed the bug. – jfs Jul 25 '12 at 23:55
  • 5
    Be careful of GCC's `__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 of ***`u = 93 - 0 - 0 - 0`*** (using the 128-bit types) as ***`18446744073709551615`*** on ARM64. – jww Apr 22 '16 at 07:45
  • There is support in C23: `printf("%w128d", my_128bit_integer);` https://thephd.dev/c-the-improvements-june-september-virtual-c-meeting – jfs Aug 06 '22 at 01:53

15 Answers15

39

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 2
    `__uint128_t` is just equivalent to `unsigned __int128`. – kennytm Jul 26 '12 at 14:26
  • 2
    @KennyTM: yes, I can see that, and know that, but there's nothing in the GCC documentation that says that (that I can see). – Jonathan Leffler Jul 26 '12 at 14:32
  • 10
    it seems `__uint128_t` and `__int128_t` are just legacy types which are now `typedef`ed to `unsigned __int128` and `__int128` respectively. Because of that, GCC just don't mention it. http://gcc.gnu.org/ml/libstdc++/2011-09/msg00068.html – kennytm Jul 26 '12 at 15:01
  • 2
    @KennyTM: Thanks for the information. I've updated the 'working code' to use the preferred modern names instead of the obsolescent and undocumented alternatives, noting that older versions of GCC only support the obsolescent notation and not the new preferred documented notation. – Jonathan Leffler Jul 26 '12 at 16:21
23

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.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • Why are they not extended integer types in the sense of C (N1256 6.2.5 "Types" I suppose) ? It is true that `sizeof(intmax_t)` gives me 8 and not 16. Why? – Ciro Santilli OurBigBook.com May 19 '15 at 15:15
  • Ah, asked for C++ at: http://stackoverflow.com/questions/21265462/why-in-g-stdintmax-t-is-not-a-int128-t Shame, as that would allow `%ju` of course. – Ciro Santilli OurBigBook.com May 19 '15 at 15:54
  • That UINT64_MAX is the longest it could be in decimal, not hex (which would be shorter, 16 hex digits of course). Btw, a clever way to the decimal version would be to use the preprocessor to generate the string by "string-izing" UINT64_MAX. – arjunyg Dec 04 '17 at 01:25
  • 1
    The problem with larger chunks is that their leading zeroes get sliced off, so you have to detect when that happens and add them back in. But [it can be done](https://stackoverflow.com/a/55970931) and yes, it can be much faster. – Gumby The Green May 03 '19 at 13:41
8

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.)

ephemient
  • 198,619
  • 38
  • 280
  • 391
  • 2
    This still requires modulus (`j % 10`), and is likely to be much more expensive than simple loop converting to decimal, largely because it requires 40*128 mod operations. You could get rid of the mod, but it would likely still be slower unless you also vectorize it do multiple digits in parallel. – Chris Dodd Jul 25 '12 at 22:28
  • 1
    @ChrisDodd I optimized the `%` away, but a benchmark on my machine shows that you're right -- this is slower after all, at least at 128 bits. It loses by less as the numbers grow larger, though... perhaps this technique is better for bignums. – ephemient Jul 26 '12 at 06:58
  • Or maybe I should try to use hardware BCD support? – ephemient Jul 26 '12 at 07:01
3

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);
Tiago Sippert
  • 1,324
  • 7
  • 24
  • 33
3

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;
} 
mosh
  • 1,402
  • 15
  • 16
2

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.

Perkins
  • 2,409
  • 25
  • 23
2

how to print __uint128_t number using gcc?
Is there PRIu128 that behaves similar to PRIu64 from :

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
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • If you need the result at the start of a buffer, the most efficient way is probably to start from the end of a local fixed-size buffer (automatic storage), then `memcpy` the result into the caller's buffer. That's more efficient than using actual recursion to not store anything until you're returning up the stack. Another optimization would be to use `uint64_t` as soon as your number fits, so (at least on 64-bit targets) you'll probably get `n%10` and `n/=10` using a multiplicative inverse instead of calling a helper function for double-width division. – Peter Cordes May 16 '18 at 21:16
  • @PeterCordes True - about recursion vs local buffer. How about using a _compound literal_ for buffer space as [How to use compound literals to `fprintf()` multiple formatted numbers with arbitrary bases?](https://stackoverflow.com/a/34292061/2410359)? – chux - Reinstate Monica May 16 '18 at 21:24
  • Yup, you could do that as the source arg for `memcpy` or `fputs` or whatever to wrap it up all on one line. – Peter Cordes May 16 '18 at 21:30
2

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("");
}
$ 
HermannSW
  • 161
  • 1
  • 8
1

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.

HermannSW
  • 161
  • 1
  • 8
  • Since maximal constant size allowed by C[++] is 64bit, macros "UINT128(h,l)" and "UINT128(h, l)" make it easy to specify 256bit constant "from left to right" as in this line "const uint256_t UINT256_MAX_10th = UINT256( UINT128(0x1999999999999999, ..." – HermannSW Jun 07 '21 at 06:28
0

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...
bumfo
  • 254
  • 3
  • 6
0

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');
    }
}
Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169
0

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


-1

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;
}
Gumby The Green
  • 603
  • 7
  • 10
-1

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

-2

much like #3

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

printf ("g = 0x%lx%lx\r\n", (uint64_t) (g >> 64), (uint64_t) g);
enzo
  • 1
  • 2
    While this code snippet may solve the question, [including an explanation](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Clijsters Dec 13 '17 at 15:47
  • 2
    It's no different from Jens Gustedt's answer, and is worse. `%lx` is not the correct way to print `uint64_t`, `PRIX64` is the one to use – phuclv Jan 14 '18 at 08:12
  • it's almost the same as user2107435's answer, too – phuclv Jan 14 '18 at 08:32
  • Code fails when `(uint64_t) (g >> 64)` is more than 0, `(uint64_t) g` should _always_ print 16 characters. It may need leading zeros. – chux - Reinstate Monica Sep 21 '21 at 15:55