How to print __int128 in g++?
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 only for that and, yet, enough to prevent to remove it totally.
My problem comes when I run into the printing parts my classes, here is a minimal example:
#include <iostream>
int main()
{
__int128 t = 1234567890;
std::cout << t << std::endl;
return t;
}
Commenting out the std::cout
line will make this code to compile nicely with g++
, but having it will cause the following error message:
int128.c: In function ‘int main()’:
int128.c:7:13: error: ambiguous overload for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘__int128’)
std::cout << t << std::endl;
^
int128.c:7:13: note: candidates are:
In file included from /usr/include/c++/4.9/iostream:39:0,
from int128.c:1:
/usr/include/c++/4.9/ostream:108:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ostream_type& (*)(std::basic_ostream<_CharT, _Traits>::__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>] <near match>
operator<<(__ostream_type& (*__pf)(__ostream_type&))
^
/usr/include/c++/4.9/ostream:108:7: note: no known conversion for argument 1 from ‘__int128’ to ‘std::basic_ostream<char>::__ostream_type& (*)(std::basic_ostream<char>::__ostream_type&) {aka std::basic_ostream<char>& (*)(std::basic_ostream<char>&)}’
/usr/include/c++/4.9/ostream:117:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ios_type& (*)(std::basic_ostream<_CharT, _Traits>::__ios_type&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>; std::basic_ostream<_CharT, _Traits>::__ios_type = std::basic_ios<char>] <near match>
operator<<(__ios_type& (*__pf)(__ios_type&))
^
/usr/include/c++/4.9/ostream:117:7: note: no known conversion for argument 1 from ‘__int128’ to ‘std::basic_ostream<char>::__ios_type& (*)(std::basic_ostream<char>::__ios_type&) {aka std::basic_ios<char>& (*)(std::basic_ios<char>&)}’
/usr/include/c++/4.9/ostream:127:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>] <near match>
operator<<(ios_base& (*__pf) (ios_base&))
^
/usr/include/c++/4.9/ostream:127:7: note: no known conversion for argument 1 from ‘__int128’ to ‘std::ios_base& (*)(std::ios_base&)’
/usr/include/c++/4.9/ostream:166:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
operator<<(long __n)
^
/usr/include/c++/4.9/ostream:170:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
operator<<(unsigned long __n)
^
/usr/include/c++/4.9/ostream:174:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(bool) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
operator<<(bool __n)
^
In file included from /usr/include/c++/4.9/ostream:609:0,
from /usr/include/c++/4.9/iostream:39,
from int128.c:1:
/usr/include/c++/4.9/bits/ostream.tcc:91:5: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short int) [with _CharT = char; _Traits = std::char_traits<char>]
basic_ostream<_CharT, _Traits>::
^
In file included from /usr/include/c++/4.9/iostream:39:0,
from int128.c:1:
/usr/include/c++/4.9/ostream:181:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(short unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
operator<<(unsigned short __n)
^
In file included from /usr/include/c++/4.9/ostream:609:0,
from /usr/include/c++/4.9/iostream:39,
from int128.c:1:
/usr/include/c++/4.9/bits/ostream.tcc:105:5: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char; _Traits = std::char_traits<char>]
basic_ostream<_CharT, _Traits>::
^
In file included from /usr/include/c++/4.9/iostream:39:0,
from int128.c:1:
/usr/include/c++/4.9/ostream:192:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
operator<<(unsigned int __n)
^
/usr/include/c++/4.9/ostream:201:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long long int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
operator<<(long long __n)
^
/usr/include/c++/4.9/ostream:205:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long long unsigned int) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
operator<<(unsigned long long __n)
^
/usr/include/c++/4.9/ostream:220:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(double) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
operator<<(double __f)
^
/usr/include/c++/4.9/ostream:224:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(float) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
operator<<(float __f)
^
/usr/include/c++/4.9/ostream:232:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(long double) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
operator<<(long double __f)
^
/usr/include/c++/4.9/ostream:245:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(const void*) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>] <near match>
operator<<(const void* __p)
^
/usr/include/c++/4.9/ostream:245:7: note: no known conversion for argument 1 from ‘__int128’ to ‘const void*’
In file included from /usr/include/c++/4.9/ostream:609:0,
from /usr/include/c++/4.9/iostream:39,
from int128.c:1:
/usr/include/c++/4.9/bits/ostream.tcc:119:5: note: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__streambuf_type*) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__streambuf_type = std::basic_streambuf<char>] <near match>
basic_ostream<_CharT, _Traits>::
^
/usr/include/c++/4.9/bits/ostream.tcc:119:5: note: no known conversion for argument 1 from ‘__int128’ to ‘std::basic_ostream<char>::__streambuf_type* {aka std::basic_streambuf<char>*}’
In file included from /usr/include/c++/4.9/iostream:39:0,
from int128.c:1:
/usr/include/c++/4.9/ostream:493:5: note: std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, unsigned char) [with _Traits = std::char_traits<char>]
operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)
^
/usr/include/c++/4.9/ostream:488:5: note: std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, signed char) [with _Traits = std::char_traits<char>]
operator<<(basic_ostream<char, _Traits>& __out, signed char __c)
^
/usr/include/c++/4.9/ostream:482:5: note: std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, char) [with _Traits = std::char_traits<char>]
operator<<(basic_ostream<char, _Traits>& __out, char __c)
^
/usr/include/c++/4.9/ostream:476:5: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, char) [with _CharT = char; _Traits = std::char_traits<char>]
operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)
^
Yes, I know, a lot of lines to explain that __int128
is just not properly handled...
Is there a simple way to get __int128
to be printed by the iostream
as any other numeric types ?
EDIT: For those who are still confusing C and C++, yes, I read the question: how to print __uint128_t number using gcc? But, this was for C and not for C++ as I am asking now.
Solution 1:
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.)
Solution 2:
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).
Solution 3:
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.