boostorg / multiprecision

Boost.Multiprecision
Boost Software License 1.0
188 stars 112 forks source link

Bug: SEGFPE on Modulus Operation with Negative Signed Numbers Greater Than 128 Bits #624

Closed itamarcps closed 1 month ago

itamarcps commented 1 month ago

Description:

We have encountered a segmentation fault (SEGFPE) when performing the modulus operation on zero with negative signed numbers greater than 128 bits using Boost Multiprecision. This issue does not occur with 128-bit numbers.

Steps to Reproduce:

  1. Define int128_t and int129_t using Boost Multiprecision with signed_magnitude and checked types.
  2. Perform modulus operation with zero on a negative number.

Sample Code:

#include <iostream>
#include <boost/multiprecision/cpp_int.hpp>

using int128_t = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<128,128, boost::multiprecision::signed_magnitude, boost::multiprecision::cpp_int_check_type::checked, void>>;
using int129_t = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<129,129, boost::multiprecision::signed_magnitude, boost::multiprecision::cpp_int_check_type::checked, void>>;

int main()
{
    int128_t ii = -42;
    std::cout << "Trying int128_t..." << std::endl;
    try { ii = ii % 0; } catch (const std::exception& e) { std::cout << "Exception: " << e.what() << std::endl; }
    std::cout << "int128_t catched..." << std::endl;

    int129_t i = -42;
    std::cout << "Trying int129_t..." << std::endl;
    try { i = i % 0; } catch (const std::exception& e) { std::cout << "Exception: " << e.what() << std::endl; }
    std::cout << "int129_t catched..." << std::endl;
}

Observed Behavior:

Expected Behavior:

The modulus operation should throw an exception for both int128_t and int129_t.

Additional Information:

#include <iostream>
#include <boost/multiprecision/cpp_int.hpp>

using int128_t = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<128,128, boost::multiprecision::signed_magnitude, boost::multiprecision::cpp_int_check_type::checked, void>>;
using int129_t = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<129,129, boost::multiprecision::signed_magnitude, boost::multiprecision::cpp_int_check_type::checked, void>>;

int main()
{
    int128_t ii = -42;
    std::cout << "Trying int128_t..." << std::endl;
    try { ii = ii / 0; } catch (const std::exception& e) { std::cout << "Exception: " << e.what() << std::endl; }
    std::cout << "int128_t catched..." << std::endl;

    int129_t i = -42;
    std::cout << "Trying int129_t..." << std::endl;
    try { i = i / 0; } catch (const std::exception& e) { std::cout << "Exception: " << e.what() << std::endl; }
    std::cout << "int129_t catched..." << std::endl;
}

Expected Output

Trying int128_t...
Exception: Division by zero.
int128_t catched...
Trying int129_t...
Exception: Integer Division by zero.
int129_t catched...

It seems that the templates picked are different for the modulus and division operations depending on the bit size of the number. Please investigate this discrepancy and provide a fix.

jzmaddock commented 1 month ago

Confirmed, will fix shortly...