【问题标题】:How to print __int128 in g++?如何在 g++ 中打印 __int128?
【发布时间】:2014-09-26 16:27:33
【问题描述】:

我在我的 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++ 的人,是的,我读到了这个问题:how to print __uint128_t number using gcc? 但是,这是针对 C 而不是针对 C++,正如我现在所问的那样。

【问题讨论】:

  • 我读过这个问题(和答案),我的问题是特别关于g++不是对于gcc...我会希望能够使用iostream,而不是printf 家族。
  • 另一个问题似乎同样适用于 C 和 C++:没有标准库支持这种类型。你必须自己创造一些东西。
  • 我期望的答案是重载运算符&lt;&lt;。您可以参考引用问题中给出的代码,但是 C++ 上下文使它完全不同(相信我)。
  • 这个问题比看起来更难,你还要考虑std::hexsetw操纵器
  • @TemplateRex 你是对的...我什至没有考虑所有这些...

标签: c++ iostream int128


【解决方案1】:

如果您不需要任何花哨的格式选项,请编写 您自己的 &lt;&lt; 运算符是微不足道的。形式上,我怀疑 为__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,但我会非常 如果不是这种情况,我会感到惊讶(正式地说,它是未定义的 行为)。如果没有,您需要修复 tmp。当然,它不处理 any 的格式 选项;您可以根据需要添加。 (处理填充和 adjustfield 正确可以是不平凡的。)

【讨论】:

  • “但如果不是这样,我会感到非常惊讶”——gcc 以基于不会发生签名溢出的假设进行积极优化而闻名,所以我不会不要和你一样惊讶。 -(value + 1) + __uint128_t(1) 之类的东西应该是有效的,并且很有可能被优化器发现为等同于 -value 的环绕版本。
  • @hvd 是的。我可能想看看生成的代码。然而,在这种特殊情况下,我从未真正看到它失败,可能是因为假设没有溢出,实际上没有任何优化可以触发。不过你的建议不错,以后类似的情况下我肯定会采纳的。 (这比我过去想出的所有变通方法都简单得多。)
  • 我想这有点牵强,但知道tmp 始终在int128_t 的范围内(因为不会发生有符号整数溢出)可能会导致tmp % 10tmp /= 10使用有符号除法指令,如果该指令比无符号版本更有效。然后由于明显的原因而失败。更有可能是用户使用-ftrapv 或等效项进行编译的调试构建,然后否定立即爆发。但是你可以提出一个论点,即如果你不支持用户不应该这样做。所以基本上,我有点同意:它可能会起作用。
  • @hvd 是的。这实际上是一个问题,即您应该在临时代码中投入多少工作,以确保合规性。 (在永久代码中,我肯定会使用更健壮的东西。写起来不那么简单,理解起来也不那么简单。)
  • 这是一个在 clang++ 中编译的例子,但在 g++ 中编译得不好。这有点做作,我不完全理解为什么 g++ 会失败(模棱两可的重载)......我尽可能地小:coliru.stacked-crooked.com/a/4314762aa663718a
【解决方案2】:

我建议不要为__int128_t 重载operator&lt;&lt;。原因是每当您看到某个整数类型的 cout &lt;&lt; x 时,您会期望像 std::hexstd::setw 这样的各种操纵器也应该工作。重载运算符时最重要的准则是:“像整数一样做”。

作为替代方案,我建议使用decimal_string(__int128_t) 函数,您可以在代码中将其用作cout &lt;&lt; decimal_string(x);。对于字符串转换,您可以使用任何 C 相关问答中的算法。这清楚地表明您有 128 位整数的特殊代码。每当标准库升级到 128 位支持时,您都可以删除它(对于这些功能,很容易grep)。

【讨论】:

  • 当你模板化整数类型时,同样的论点正是为什么一个应该重载operator&lt;&lt;__int128的行为应该和int一样,所以有应该支持operator&lt;&lt;
  • @HolKann 如果你重载operator&lt;&lt;,你最好确保你也支持各种操纵器,如hexsetw
【解决方案3】:

股票cout 不处理__int128,但您可以使用自己的函数对其进行扩展。

首先,编写如下代码:

std::ostream& operator<<(std::ostream& os, __int128 t) {
    // TODO: Convert t to string
    return os << str;
}

128位数字转字符串的SO有很多解决方案,这里不再赘述。

评论中关于库的兼容性:

如果标准库没有提供这样的处理程序,你只需要滚动你自己的函数。一旦库支持该类型,您应该会在构建时看到冲突,例如 [注意:内置候选运算符

【讨论】:

  • 什么字符串?十进制表示还是十六进制?这与例如标准操纵器不兼容,因此可能令人惊讶
  • 我认为这是最有希望的答案。然后,我需要将它与answer 结合起来,它应该可以工作。谢谢
  • 您至少应该提到它是未定义的行为,并且一旦库添加了对该类型的适当支持,它就会中断并且必须停用。 (如果你想正确地处理所有的标志,这绝对不是微不足道的。)
  • @TemplateRex 是否兼容标准机械手取决于他的TODO内容。我编写了兼容的插入运算符(尽管它涉及很多额外的代码)。不过既然基本上是临时解决方案,他可能不需要完全兼容;我只会根据需要添加支持。
  • @JamesKanze 因为它是临时的,我建议编写一个函数 string_int128 以强调它不是成熟的可打印类型。用法为cout &lt;&lt; string_int128(x) 重载运算符时,应努力“像整数一样”。
【解决方案4】:

看似简单的方法

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');
}

【讨论】:

  • 递归可以很简洁,但它会给堆栈增加很多压力(虽然在这种情况下它受限于整数的大小,它是一个相当大的整数......)
  • 如果数字是0x800...000-x 也是假的,因为x == -x 所以它仍然是负数。
  • 是的,你是对的,这在最坏的情况下可以将堆栈增加 40。这绝对不是最有效的方法。我已经修复了边缘情况。
【解决方案5】:

到目前为止的答案都很好,但我只是想补充一下 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);
}

【讨论】:

  • 由于某种原因,标志恢复似乎无法正常工作。我不知道到底是什么问题,但是如果有人弄清楚了,请告诉我
  • tu lodu hai kya be!
【解决方案6】:

如果它不是性能关键,这里有一个简单易读的方法将 非负 int128 转换为 base-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;
}

注意:我还为 unsigned int128s here 发布了此答案的一个版本。

【讨论】:

  • 您的第一种方法不适用于负数。
  • @HolKann 这是真的,我只是添加了一个注释来澄清。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-09
  • 2014-03-25
  • 1970-01-01
相关资源
最近更新 更多