私はC++プログラムのいくつかのことにGCCビルトインタイプ__int128
を使用していますが、実際には重要なことは何もありません。完全に。
私の問題は、クラスの印刷部分に遭遇したときに発生します。最小の例を次に示します。
#include <iostream>
int main()
{
__int128 t = 1234567890;
std::cout << t << std::endl;
return t;
}
std::cout
行をコメントアウトすると、このコードはg++
で適切にコンパイルされますが、それがあると次のエラーメッセージが表示されます。
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)
^
はい、私は知っています、__int128
が適切に処理されていないことを説明するための行がたくさんあります...
他の数値型としてiostream
で__int128
を出力するsimple方法はありますか?
編集:まだCとC++を混乱させている人のために、はい、私は質問を読みました: gccを使用して__uint128_t数を印刷する方法? しかし、これはC++ではなくC用でした私は今尋ねています。
凝った書式設定オプションを必要としない場合、独自の<<
演算子を書くのは簡単です。正式には、__int128_t
の記述は未定義の動作と見なされると思いますが、実際には、ライブラリが実際のサポートを提供し始めるまで動作すると思います(この時点で、変換演算子は廃止されます) )。
とにかく、次のようなものが動作するはずです:
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;
}
これは、g ++ライブラリがその型をサポートするまで、ただの一時的な修正であることに注意してください。 __int128_t
の場合、2の補数、オーバーフローでラップアラウンドをカウントしますが、そうでない場合は非常に驚くでしょう(正式には未定義の動作です)。そうでない場合は、tmp
の初期化を修正する必要があります。そしてもちろん、フォーマットオプションのanyを処理しません。必要に応じて追加できます。 (パディングとadjustfield
を正しく処理するのは簡単ではありません。)
_operator<<
_の___int128_t
_をオーバーロードしないことをお勧めします。その理由は、整数型の_cout << x
_が表示されるときはいつでも、_std::hex
_や_std::setw
_などのあらゆる種類のマニピュレーターが動作するはずだということです。演算子をオーバーロードするときの最も重要なガイドラインは、「intが行うように行う」です。
別の方法として、コードでdecimal_string(__int128_t)
として使用できるcout << decimal_string(x);
関数を使用することをお勧めします。文字列の変換には、C関連のQ&Aのアルゴリズムを使用できます。これにより、128ビット整数用の特別なコードがあることが明らかになります。標準ライブラリが128ビットサポートにアップグレードされるたびに、それを削除できます(これらの関数のgrep
は簡単です)。
ストックcout
は__int128
を処理しませんが、独自の関数で拡張できます。
まず、次のようなコードを作成します。
std::ostream& operator<<(std::ostream& os, __int128 t) {
// TODO: Convert t to string
return os << str;
}
128ビット数を文字列に変換するためのSOには多くの解決策があります。ここでは繰り返しません。
コメント内のライブラリの互換性について:
標準ライブラリがそのようなハンドラを提供しない場合にのみ、独自の関数をロールする必要があります。ライブラリが型をサポートすると、ビルド時に競合が表示されるはずです。[注:組み込み候補operator <<]のように、int64_tで試してください。
パフォーマンスが重要ではない場合、int128を10進数の文字列に変換するための簡単で読みやすい方法を次に示します(もちろん印刷できます)。
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;
}
数字を一度に1つではなく、より大きなチャンクで取得することで、これを数倍速くすることができます。ただし、失われた先行ゼロがないか各チャンクをチェックし、それらを再び追加する必要があります。
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;
}
注:unsignedint128s here についても、この回答のバージョンを投稿しました。