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.