如何在g++中打印__int128
How to print __int128 in g++?
我在C++程序中使用GCC内置类型__int128
做了一些事情,没有什么真正重要的,至少不足以证明只为此使用BigInt库是合理的,但也足以防止完全删除它。
当我遇到我的类的打印部分时,我的问题就来了,这里有一个最小的例子:
#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
只是处理不当。。。
是否有简单的方法让__int128
由iostream
打印为任何其他数字类型?
编辑:对于那些仍然混淆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++库支持该类型。它依赖于2的补码,对于__int128_t
,在溢出时进行包装,但我会非常如果不是这样的话,我会感到惊讶(从形式上讲,这是未定义的行为(。如果没有,则需要修复的初始化CCD_ 10。当然,它不处理任何格式选项;您可以根据需要添加。(处理衬垫和adjustfield
正确地可以是非平凡的。(
我建议不要为__int128_t
重载operator<<
。原因是,每当您看到某个整数类型的cout << x
时,您都会期望所有类型的操纵器(如std::hex
或std::setw
(也能工作。重载运算符时最重要的准则是:"像int那样做"。
作为替代方案,我建议使用decimal_string(__int128_t)
函数,您可以在代码中使用该函数作为cout << decimal_string(x);
。对于字符串转换,您可以使用来自任何C相关的Q&这清楚地表明,您有针对128位int的特殊代码。每当标准库升级到128位支持时,您都可以将其删除(对于这些功能,grep
很容易(。
库存cout
不处理__int128
,但您可以使用自己的函数对其进行扩展。
对于启动器,代码如下:
std::ostream& operator<<(std::ostream& os, __int128 t) {
// TODO: Convert t to string
return os << str;
}
SO上有很多将128位数字转换为字符串的解决方案,我在此不再重复。
关于注释中的库兼容性:
如果标准库没有提供这样的处理程序,则只需要滚动自己的函数。一旦库支持该类型,那么在构建时应该会看到冲突,比如[注意:内置候选运算符<<],请尝试使用int64_t。
一种看似简单的方法
std::ostream& operator<<(std::ostream& o, const __int128& x) {
if (x == std::numeric_limits<__int128>::min()) return o << "-170141183460469231731687303715884105728";
if (x < 0) return o << "-" << -x;
if (x < 10) return o << (char)(x + '0');
return o << x / 10 << (char)(x % 10 + '0');
}
注意到过载<<
可能会产生误导的警告,因为可能没有提供一些预期的操作支持,这里有一个支持操作的版本:
#include <iostream>
#include <iomanip>
// Write the 128-bit integer val to out, with a minus sign if decimal and neg
// is true. Obey all of the ostream settings of out for integer display: octal
// or hexadecimal, upper case letters, plus sign, fill character and width, and
// fill placement.
static void out128(std::ostream& out, __uint128_t val, int neg) {
// Note if the number is zero. (No hex or octal prefix in this case.)
auto zero = val == 0;
// Note if upper-case letters requested.
auto state = out.flags();
auto upper = (state & std::ios_base::uppercase) != 0;
// Set base for digits.
unsigned base = state & std::ios_base::hex ? 16 :
state & std::ios_base::oct ? 8 :
10;
// Space for digits and prefix. Generate digits starting at the end of the
// string, going backwards. num will be the digit string. Terminate it.
char str[47];
auto end = str + sizeof(str), num = end;
*--num = 0;
// Compute and place digits in base base.
do {
char dig = val % base;
val /= base;
dig += dig < 10 ? '0' : (upper ? 'A' : 'a') - 10;
*--num = dig;
} while (val);
// Prepend octal number with a zero if requested.
if (state & std::ios_base::showbase && base == 8 && !zero)
*--num = '0';
// pre will be the prefix string. Terminate it.
auto pre = num;
*--pre = 0;
// Put a plus or minus sign in the prefix as appropriate.
if (base == 10) {
if (neg)
*--pre = '-';
else if (state & std::ios_base::showpos)
*--pre = '+';
}
// Prefix a hexadecimal number if requested.
else if (state & std::ios_base::showbase && base == 16 && !zero) {
*--pre = upper ? 'X' : 'x';
*--pre = '0';
}
// Compute the number of pad characters and get the fill character.
auto len = (num - pre) + (end - num) - 2;
auto pad = out.width();
out.width(0);
pad = pad > len ? pad - len : 0;
char fill = out.fill();
// Put the padding before prefix if neither left nor internal requested.
if (!(state & (std::ios_base::internal | std::ios_base::left)))
while (pad) {
out << fill;
pad--;
}
// Write prefix.
out << pre;
// Put the padding between the prefix and the digits if requested.
if (state & std::ios_base::internal)
while (pad) {
out << fill;
pad--;
}
// Write digits.
out << num;
// Put number to the left of padding, if requested.
if (state & std::ios_base::left)
while (pad) {
out << fill;
pad--;
}
}
// Overload << for an unsigned 128-bit integer.
std::ostream& operator<<(std::ostream& out, __uint128_t val) {
out128(out, val, 0);
return out;
}
// Overload << for a signed 128-bit integer. Negation of the most negative
// signed value gives the correct unsigned absolute value.
std::ostream& operator<<(std::ostream& out, __int128_t val) {
auto state = out.flags();
if (val < 0 && !(state & (std::ios_base::hex | std::ios_base::oct)))
out128(out, (__uint128_t)-val, 1);
else
out128(out, (__uint128_t)val, 0);
return out;
}
如果它不是性能关键型的,这里有一种简单可读的方法可以将非负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;
}
我们可以通过将数字分成更大的块而不是一次一个来使速度快几倍。但它要求我们检查每个区块是否有丢失的前导零,并将其添加回
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的答案版本。
到目前为止的答案都很好,但我只是想补充James Kanze的答案。首先注意,由于无符号转换,它不适用于数字-0x80000000000000000000000000000000
。其次,您可以利用64位整数打印的优点,优化功能实现,如下所示:
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);
}
- 如何循环打印顶点结构
- 为什么在popback()操作之后,它仍然打印完整的矢量
- 如何在c++中打印目录
- 有一个打印语句的函数是一种糟糕的编程实践吗
- 在线编译器中的分段C++没有打印消息
- 在C++中打印指向不同基元数据类型的指针的内存地址
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- 如何将结构插入到集合中并打印集合的成员
- 在循环C++中指定字符串之后,不会打印该字符串
- 以螺旋方式打印矩阵的程序.(工作不好)
- 从控制台中删除最后打印的元素
- 如何使用Crypto++并为RSA返回可打印的字节/字符数组
- 如何仅使用对象名称打印特定于对象的成员
- 回溯C++不打印函数,因此文件
- 在一定长度后从数组中打印时缺少整数
- 为什么这个 c++ 代码打印出长度 5,当我打印出字符串时,程序会自动终止?
- 在gem5中打印文件中的所有cache_blocks
- 打印数字图案
- Log4cpp:以UTC/GMT时区打印日期
- 如何使用gdb制作一个可以漂亮地打印每个对象的C++函数