C++ 如何在 g++ 中打印 __int128?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25114597/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 11:15:06  来源:igfitidea点击:

How to print __int128 in g++?

c++iostreamint128

提问by perror

I am using the GCC built-in type __int128for 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.

__int128在我的 C++ 程序中将 GCC 内置类型用于一些事情,没有什么真正重要的,至少不足以证明只为此使用 BigInt 库是合理的,但足以防止完全删除它。

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::coutline will make this code to compile nicely with g++, but having it will cause the following error message:

注释掉该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)
     ^

Yes, I know, a lot of lines to explain that __int128is just not properly handled...

是的,我知道,很多行来解释__int128只是没有正确处理......

Is there a simpleway to get __int128to be printed by the iostreamas any other numeric types ?

有没有一种简单的方法__int128可以iostream像任何其他数字类型一样被打印?

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.

编辑:对于那些仍然对 C 和 C++ 感到困惑的人,是的,我读到了一个问题:如何使用 gcc 打印 __uint128_t 数字?但是,这是针对 C 而不是针对我现在问的 C++。

采纳答案by James Kanze

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_twould 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).

如果您不需要任何花哨的格式选项,编写您自己的<<运算符是微不足道的。正式地说,我怀疑编写一个 for__int128_t将被视为未定义的行为,但实际上,我认为它会起作用,直到库开始为其提供实际支持(此时,您将退休您的转换运算符)。

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 anyof the formatting options; you can add as desired. (Handling padding and the adjustfieldcorrectly can be non-trivial.)

请注意,这只是一个快速的临时修复,直到 g++ 库支持该类型为止。它依赖于 2 的补码,环绕溢出, for __int128_t,但如果情况并非如此,我会感到非常惊讶(正式而言,这是未定义的行为)。如果没有,您需要修复 tmp. 当然,它不处理任何格式选项;您可以根据需要添加。(处理填充和adjustfield正确处理 可能非常重要。)

回答by TemplateRex

I would recommend against overloading operator<<for __int128_t. The reason is that whenever you see cout << xfor some integer type, you'd expect that all kinds of manipulators like std::hexor std::setwshould also work. The most important guideline when overloading operators is: "do as the ints do".

我建议不要operator<<__int128_t. 原因是当你看到cout << x某种整数类型时,你会期望所有类型的操纵器都喜欢std::hexstd::setw应该也能工作。重载运算符时最重要的准则是:“像整数一样做”。

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 grepfor these functions).

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

回答by Non-maskable Interrupt

The stock coutdoes not handle __int128, but you may extends it with your own function.

股票cout不处理__int128,但你可以用你自己的函数扩展它。

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.

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

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.

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

回答by HolKann

The most efficient version I could come up with

我能想到的最有效的版本

std::ostream& operator<<(std::ostream& os, __int128 x){
    if(x<0){ os << "-"; x = -x; }
    uint64_t tenPow18 = 1000000000000000000;
    uint64_t x1 = x%tenPow18;
    x/=tenPow18;
    if(x>0){
        uint64_t x2 = x%tenPow18;
        x/=tenPow18;
        if(x>0) os << (short) x;
        os << x2;
    }
    return os << x1;
}

just prints two 64 bit and one 16 bit integer. This boils down to an unrolled and simplified version of @Gumby The Green and @HackerBoss's answer.

只打印两个 64 位和一个 16 位整数。这归结为@Gumby The Green 和@HackerBoss 答案的展开和简化版本。

The above code is wrong, as it does not take leading zeros into account for intermediate prints. E.g. "1000000000000000001" will be printed as "11". @Gumby The Green and @HackerBoss's answer probably suffer from this as well.

上面的代码是错误的,因为它没有考虑中间打印的前导零。例如,“1000000000000000001”将被打印为“11”。@Gumby The Green 和@HackerBoss 的回答也可能受到此影响。

In the end, I went with the answer in my other comment.

最后,我在另一条评论中给出了答案。

回答by Gumby The Green

If it's not performance-critical, here's a simple, readable way to convert a non-negativeint128 to a base-10 string (which can then be printed of course):

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

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 unsignedint128s here.

注意:我还在此处unsignedint128s发布了此答案的一个版本。

回答by HackerBoss

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:

到目前为止的答案都很好,但我只是想补充一下 James Kanze 的答案。首先请注意,由于无符号转换,它不适用于 number -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);
}

回答by HolKann

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.

有关更高性能实现的失败尝试,请参阅我的其他评论。