how to print __uint128_t number using gcc?
Asked Answered
B

15

66

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.

Bernardo answered 25/7, 2012 at 18:28 Comment(17)
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.Bulbous
@DanielFischer: char str[40] = {0}; filled the whole array with zero already.Oleaster
@Wug: yes. Normally I would. It is just an example to avoid the boiler-plate with passing buffers around.Bernardo
@KennyTM Oh, duh! How did I overlook that? Thanks for the correction.Morphia
you could print it with two (or three) uint64_tZoril
Where is uint128_t defined? Normally <inttypes.h> should define both uintN_t and PRIuN. (Mine only goes up to 64.)Bichromate
@KeithThompson: __uint128_t is available in gcc. It is not defined in <inttypes.h>.Bernardo
@KarolyHorvath: do you mean something like this?Bernardo
@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 <inttypes.h> defines uint128_t, it should also define PRIu128.Bichromate
@KeithThompson: gcc version - 4.6.3. I've used typedef __uint128_t uint128_t.Bernardo
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.Bichromate
@J.F. Sebastian: something like that, though for the lower "digits" you need %.13Zoril
@Karoly Horvath: I don't understand. It works for all uint128_t numbers.Bernardo
@J.F. Sebastian: if it works for 4 numbers, it works for all of them? :) print(1000000000000000000LL);Zoril
@KarolyHorvath: yes. It should work for all 340282366920938463463374607431768211456 of them. ;) I've fixed the bug.Bernardo
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.Biddick
There is support in C23: printf("%w128d", my_128bit_integer); thephd.dev/c-the-improvements-june-september-virtual-c-meetingBernardo
E
24

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.

Emergent answered 25/7, 2012 at 21:21 Comment(4)
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?Hollo
Ah, asked for C++ at: stackoverflow.com/questions/21265462/… Shame, as that would allow %ju of course.Hollo
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.Primipara
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 and yes, it can be much faster.Alti
B
40

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.

Brigitta answered 26/7, 2012 at 0:23 Comment(4)
__uint128_t is just equivalent to unsigned __int128.Oleaster
@KennyTM: yes, I can see that, and know that, but there's nothing in the GCC documentation that says that (that I can see).Brigitta
it seems __uint128_t and __int128_t are just legacy types which are now typedefed to unsigned __int128 and __int128 respectively. Because of that, GCC just don't mention it. gcc.gnu.org/ml/libstdc++/2011-09/msg00068.htmlOleaster
@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.Brigitta
E
24

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.

Emergent answered 25/7, 2012 at 21:21 Comment(4)
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?Hollo
Ah, asked for C++ at: stackoverflow.com/questions/21265462/… Shame, as that would allow %ju of course.Hollo
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.Primipara
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 and yes, it can be much faster.Alti
R
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.)

Ramos answered 25/7, 2012 at 22:18 Comment(3)
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.Theatricals
@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.Ramos
Or maybe I should try to use hardware BCD support?Ramos
K
4

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;
} 
Kimberykimble answered 4/7, 2017 at 15:46 Comment(1)
This does not work for 0x80000000000000000000000000000000, i.e. the minimum value. The reason is that for the minimum value, n == -n.Amyloid
R
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);
Redbird answered 21/6, 2013 at 13:49 Comment(1)
this is for printing hexadecimals, not decimalsSuh
E
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.

Earthnut answered 15/3, 2014 at 4:18 Comment(0)
C
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
Czerny answered 25/2, 2018 at 5:44 Comment(3)
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.Thermos
@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??Czerny
Yup, you could do that as the source arg for memcpy or fputs or whatever to wrap it up all on one line.Thermos
S
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("");
}
$ 
Suziesuzuki answered 5/6, 2021 at 0:54 Comment(1)
Good idea. Minor micro-optimization: Quotient u/d38_ in in unsigned range. else { pu64(u/d38_); ... --> else { printf("%u", (unsigned) (u/d38_)); ....Czerny
S
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.

Suziesuzuki answered 7/6, 2021 at 2:21 Comment(1)
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, ..."Suziesuzuki
S
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...
Squeegee answered 16/1, 2017 at 6:5 Comment(0)
F
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');
    }
}
Feel answered 1/2, 2018 at 10:16 Comment(0)
I
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;
}


Imbroglio answered 20/1, 2023 at 23:58 Comment(0)
A
-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;
}
Alti answered 3/5, 2019 at 13:34 Comment(6)
@jfs, true. I've added a note.Alti
You can also play with setw/setfill if you like streams.Scherzo
std::to_string is slowNurserymaid
@PSPCODER Is there a faster function in the standard library?Alti
std::to_string is slow when compiled with -O0 *Nurserymaid
@PSPCODER I always compile with -O3 but that's good to know for those who don't.Alti
J
-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;
}

Jacquez answered 8/6, 2021 at 7:7 Comment(1)
The OP uses C, not C++.Titustityus
S
-2

much like #3

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

printf ("g = 0x%lx%lx\r\n", (uint64_t) (g >> 64), (uint64_t) g);
Sopping answered 13/12, 2017 at 15:35 Comment(4)
While this code snippet may solve the question, including an explanation 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.Smail
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 useSuh
it's almost the same as user2107435's answer, tooSuh
Code fails when (uint64_t) (g >> 64) is more than 0, (uint64_t) g should always print 16 characters. It may need leading zeros.Czerny

© 2022 - 2024 — McMap. All rights reserved.