0

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?

amillerp
  • 11
  • 3
  • 2
    Even though it might not seem necessary, some code demonstrating the issue would be appreciated. I also assume that the library is documented on boost's site. Did you check that out at all? – sweenish Aug 03 '23 at 00:51
  • I could not reproduce, comparing the int256_t to -1 equated to **true**. A [mcve] would be very helpful. I suspect your code has a bug, that I did not reproduce in my code. Also, thank you for bringing Boost **cpp_int** to my attention, something I was not aware of, and definitely will come in handy for my work. – Eljay Aug 03 '23 at 12:46
  • A minimal reproducer is mandatory here. For one thing, you completely fail to specify what you mean by "cast". Do you mean static_cast (like all other commenters/answerers)? I have a suspicion you might be reinterpret_casting (perhaps using C-style cast) – sehe Aug 03 '23 at 13:52

1 Answers1

3

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
sehe
  • 374,641
  • 47
  • 450
  • 633
Andrey Semashev
  • 10,046
  • 1
  • 17
  • 27
  • What kind of cast are you imagining when you describe its behaviour? A bit of code would help see whether the behaviour is as-documented or accidental. IME proper conversion with BMP uses `number<>::convert_to` – sehe Aug 03 '23 at 13:53
  • 1
    @sehe I'm considering code like this: https://godbolt.org/z/djYa56GjP – Andrey Semashev Aug 03 '23 at 14:12