I am using the GCC built-in type __int128 for a few things in my C++ program, nothing really significant, at least not enough to justify to use BigInt library o
If you don't need any of the fancy formatting options, writing
your own << operator is trivial. Formally, I suspect that
writing one for __int128_t would be considered undefined
behavior, but practically, I think it would work, up until the
library starts providing actual support for it (at which point,
you'd retire your conversion operator).
Anyway, something like the following should work:
std::ostream&
operator<<( std::ostream& dest, __int128_t value )
{
std::ostream::sentry s( dest );
if ( s ) {
__uint128_t tmp = value < 0 ? -value : value;
char buffer[ 128 ];
char* d = std::end( buffer );
do
{
-- d;
*d = "0123456789"[ tmp % 10 ];
tmp /= 10;
} while ( tmp != 0 );
if ( value < 0 ) {
-- d;
*d = '-';
}
int len = std::end( buffer ) - d;
if ( dest.rdbuf()->sputn( d, len ) != len ) {
dest.setstate( std::ios_base::badbit );
}
}
return dest;
}
Note that this is just a quicky, temporary fix, until the time
the g++ library supports the type. It counts on 2's complement,
wrap around on overflow, for __int128_t, but I'd be very
surprised if that wasn't the case (formally, it's undefined
behavior). If not, you'll need to fix up the initialization of
tmp. And of course, it doesn't handle any of the formatting
options; you can add as desired. (Handling padding and the
adjustfield correctly can be non-trivial.)
I would recommend against overloading operator<< for __int128_t. The reason is that whenever you see cout << x for some integer type, you'd expect that all kinds of manipulators like std::hex or std::setw should also work. The most important guideline when overloading operators is: "do as the ints do".
As an alternative, I would recommend using a decimal_string(__int128_t) function that you can use as cout << decimal_string(x); in your code. For the string conversion, you can use the algorithm from any of the C-related Q&As. This makes it clear that you have special code for your 128-bit ints. Whenever the Standard Library upgrades to 128-bit support, you can drop it (and it's easy to grep for these functions).
The answers so far are good, but I just wanted to add to the answer from James Kanze. Firstly note that because of the unsigned conversion, it will not work for the number -0x80000000000000000000000000000000. Secondly, you can advantage the fact that printing with 64-bit integers works, to optimize the function implementation as follows:
std::ostream& operator<<(std::ostream& os, __int128_t value) {
if (value < 0) {
os << '-';
value = -value;
}
// save flags to restore them
std::ios_base::fmtflags flags(os.flags());
// set zero fill
os << std::setfill('0') << std::setw(13);
// 128-bit number has at most 39 digits,
// so the below loop will run at most 3 times
const int64_t modulus = 10000000000000; // 10**13
do {
int64_t val = value % modulus;
value /= modulus;
if (value == 0) {
os.flags(flags);
return os << val;
}
os << val;
} while (1);
}
If it's not performance-critical, here's a simple, readable way to convert a non-negative int128 to a base-10 string (which can then be printed of course):
std::string toString(__int128 num) {
std::string str;
do {
int digit = num % 10;
str = std::to_string(digit) + str;
num = (num - digit) / 10;
} while (num != 0);
return str;
}
We can make this 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(__int128 num) {
auto tenPow18 = 1000000000000000000;
std::string str;
do {
long long digits = num % tenPow18;
auto digitsStr = std::to_string(digits);
auto leading0s = (digits != num) ? std::string(18 - digitsStr.length(), '0') : "";
str = leading0s + digitsStr + str;
num = (num - digits) / tenPow18;
} while (num != 0);
return str;
}
Note: I've also posted a version of this answer for unsigned int128s here.
The stock cout does not handle __int128, but you may extends it with your own function.
For starter, code something like this:
std::ostream& operator<<(std::ostream& os, __int128 t) {
// TODO: Convert t to string
return os << str;
}
There are many solution on SO to convert 128 bit number to string, I'll not repeat here.
About library compatibility in comment:
You only need to roll your own function if the standard library does not provide such handler. Once the library support the type, you should then see a conflict when building, something like [ note: built-in candidate operator<< ], go try that with int64_t.
A deceptively simple approach
std::ostream& operator<<(std::ostream& os, __int128 x){
if(x<0) return os << "-" << -x;
if(x<10) return os << (char)(x+'0');
return os << x/10 << (char)(x%10+'0');
}
See my other comment for a failed attempt at a more performant implementation.