How to print __int128 in g++?

前端 未结 6 1753
再見小時候
再見小時候 2020-12-05 06:48

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 o

相关标签:
6条回答
  • 2020-12-05 06:54

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

    0 讨论(0)
  • 2020-12-05 06:57

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

    0 讨论(0)
  • 2020-12-05 06:57

    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:

    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);
    }
    
    0 讨论(0)
  • 2020-12-05 07:01

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

    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 unsigned int128s here.

    0 讨论(0)
  • 2020-12-05 07:08

    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.

    0 讨论(0)
  • 2020-12-05 07:11

    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.

    0 讨论(0)
提交回复
热议问题