c++boostintegertwos-complementuint

int256_t using C++ boost library capable of showing 2^{256} - 1


I'm using the cpp_int header file from boost/multiprecision and for some reason when I cast -1 to a uint256_t then cast it back to an int256_t, it keeps the same value (~1.15e77). When I increment this variable, it wraps back to 0. What gives. Why is my int256_t acting like a uint256_t?

Apologies for bad formatting, I'm not used to using stack overflow.

I tried printing the int256_t and it printed the same value as the uint26_t, which it shouldn't be able to do. I also tried printing int256_t(-1) and it printed -1 fine. If I compare the int256_t to -1 it equates to false, but if I increment them both and then compare it it equates to true as they're both now 0.

Why is this the case? The max uint256_t should be 0xfff....fff, and a signed 2s complement version of -1 in int256_t is 0xfff....fff as well. Is the boost implementation of integers not 2s complement?


Solution

  • int256_t uses signed magnitude format for number representation, meaning that it separately stores the information about sign and magnitude of the number:

    The type uses a sign-magnitude representation internally, so type int128_t has 128-bits of precision plus an extra sign bit. In this respect the behaviour of these types differs from fundamental (built-in) 2's complement types.

    When you cast int256_t to uint256_t, you strip information about the sign and get the number that is equal to "0 + the signed number". For negatives, this causes a wrap, so for -1 you get numeric_limits<uint256_t>::max(). When you cast back, the sign is assumed positive, since by definition unsigned numbers are always positive. The magnitude part of int256_t is capable of holding the same number of bits as uint256_t, so the conversion is without loss.

    Demonstrating:

    Live On Compiler Explorer

    #include <iostream>
    #include <boost/multiprecision/cpp_int.hpp>
    
    namespace mp = boost::multiprecision;
    
    int main()
    {
        mp::int256_t si1{-1};
        mp::uint256_t ui1{si1};
        mp::int256_t si2{ui1};
    
        std::cout << si1 << "\n" << ui1 << "\n" << si2 << std::endl;
    }
    

    Prints

    -1
    115792089237316195423570985008687907853269984665640564039457584007913129639935
    115792089237316195423570985008687907853269984665640564039457584007913129639935