How to catch boost::wrapexcept from cpp_int?

504 views Asked by At

For some reason there was a change of behavior between boost 1.78 and 1.79 causing now cpp_int convert_to<double> conversion throwing exceptions for very big numbers. Sometimes it is overflow_error, sometimes domain_error.

I have not found if it is intentional change or not, but right now as a quick first step I would like just to react on this and preserve the old behavior of our code, which was giving infinity for such numbers, std::numeric_limits<double>::max.

But I have trouble catching the exception. How can I catch instance of boost::wrapexcept<std::overflow_error>? Is some special handling required? Following simple code - see behavior comparison boost 78 vs 79 here - is not catching the exception but just terminates with terminate called after throwing an instance of 'boost::wrapexceptstd::overflow_error' what(): Error in function float_next(double): Overflow Error

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

int main() {
    std::string strNumber = "179769313486231590617005494896502488139538923424507473845653439431848569886227202866765261632299351819569917639009010788373365912036255753178371299382143631760131695224907130882552454362167933328609537509415576609030163673758148226168953269623548572115351901405836315903312675793605327103910016259918212890625";
    boost::multiprecision::cpp_int number(strNumber);

    try {
        std::cout << number.convert_to<double>() << "\n";
    } catch (boost::wrapexcept<std::overflow_error> &e) {
        std::cout << "boost exception caught" << "\n";
    } catch (...) {
        std::cout << "some exception caught" << "\n";
    }
    
    std::cout << "finished" << "\n";
}

I am expecting at least "some exception caught" line to be called, but it is not. Thanks for help.

Note after reading some answers: The code is just an example. My current concern is not to convert the number to double. That is another following issue. More important for me right now is actually to catch the exception somehow so that I can return std::numeric_limits<double>::max() instead of exception and the existing program will work the same way as with older boost (breaking change concerns).

Note 2: This seems to be a bug in boost, caused by throwing an exception in noexcept method. That terminates the program without possibility to catch the exception. Thanks @Ext3h for help. I reported the thing in boost and will properly answer and close this issue if confirmed.

2

There are 2 answers

7
Ext3h On

Don't try to catch the wrapper - it's inheriting from std::overflow_error and that's what you should go and catch.

Also make sure you are catching the exception as const std::overflow_error& - your are running into a whole series of unexpected side effects if you ever try to catch an exception as anything other than a const reference. Namely, the compiler will have to copy the exception just for your exception handler, which may fail or lead to inefficient behavior for various reasons.

Finally, the issue you are still having is that the line boost::multiprecision::cpp_int number(strNumber); is potentially already throwing an exception.

Check the exception specification: https://www.boost.org/doc/libs/1_80_0/libs/multiprecision/doc/html/boost_multiprecision/tut/ints/cpp_int.html

5
gera verbun On

To avoid try catch don't use convert_to like you did and instead of do this Boost Multiprecision float, Constructing and Interconverting Between Number Types:

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

namespace mp = boost::multiprecision;

int main() {
    std::string strNumber = "179769313486231590617005494896502488139538923424507473845653439431848569886227202866765261632299351819569917639009010788373365912036255753178371299382143631760131695224907130882552454362167933328609537509415576609030163673758148226168953269623548572115351901405836315903312675793605327103910016259918212890625";
    mp::cpp_int number(strNumber);

    std::cout << "Boost Multiprecision: "
                << std::setprecision(std::numeric_limits<mp::cpp_dec_float_50>::digits10)
                << number << std::endl;

    std::cout << "Double: "
                << std::setprecision(std::numeric_limits<double>::digits10)
                << number << std::endl;
}

Output:

Boost Multiprecision: 179769313486231590617005494896502488139538923424507473845653439431848569886227202866765261632299351819569917639009010788373365912036255753178371299382143631760131695224907130882552454362167933328609537509415576609030163673758148226168953269623548572115351901405836315903312675793605327103910016259918212890625
Double: 179769313486231590617005494896502488139538923424507473845653439431848569886227202866765261632299351819569917639009010788373365912036255753178371299382143631760131695224907130882552454362167933328609537509415576609030163673758148226168953269623548572115351901405836315903312675793605327103910016259918212890625

Like that you don't need try catch.

Or you can How to convert from boost::multiprecision::cpp_int to cpp_dec_float<0>:

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

namespace mp = boost::multiprecision;

int main() {
    using Int = mp::cpp_int;
    std::string strNumber = "179769313486231590617005494896502488139538923424507473845653439431848569886227202866765261632299351819569917639009010788373365912036255753178371299382143631760131695224907130882552454362167933328609537509415576609030163673758148226168953269623548572115351901405836315903312675793605327103910016259918212890625";
    Int number(strNumber);

    using Dec = mp::number<mp::cpp_dec_float<0>>;
    std::cout << number.convert_to<Dec>();
}

Output:

1.79769e+308

Or if you need try catch do the next thing:

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

int main() {
    std::string strNumber = "179769313486231590617005494896502488139538923424507473845653439431848569886227202866765261632299351819569917639009010788373365912036255753178371299382143631760131695224907130882552454362167933328609537509415576609030163673758148226168953269623548572115351901405836315903312675793605327103910016259918212890625";
    boost::multiprecision::cpp_int number(strNumber);

    try {
        std::cout << number.convert_to<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<0>>>() << "\n";
    } catch (boost::wrapexcept<std::overflow_error> const& e) {
        std::cout << "boost exception caught" << "\n";
    } catch (...) {
        std::cout << "some exception caught" << "\n";
    }

    std::cout << "finished" << "\n";
}

Output:

1.79769e+308
finished