jfs
jfs

Reputation: 414745

how to print __uint128_t number using gcc?

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

Answers (16)

chux
chux

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

mdsiaofficial
mdsiaofficial

Reputation: 80

Easiest way to print unsigned int 128 bit or int 128 bit:

To print __int128, you must use __int128_tdata 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 %llufor __uint128_t.

in C++, std::coutprinting... 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

David Shang
David Shang

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

Sergey Volnov
Sergey Volnov

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

HermannSW
HermannSW

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

HermannSW
HermannSW

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

Gumby The Green
Gumby The Green

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

Tomilov Anatoliy
Tomilov Anatoliy

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

enzo
enzo

Reputation: 1

much like #3

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

printf ("g = 0x%lx%lx\r\n", (uint64_t) (g >> 64), (uint64_t) g);

Upvotes: -2

mosh
mosh

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

bumfo
bumfo

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

Jens Gustedt
Jens Gustedt

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

Perkins
Perkins

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

user2107435
user2107435

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

Jonathan Leffler
Jonathan Leffler

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

Upvotes: 43

ephemient
ephemient

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

Related Questions