1

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.

MartinD
  • 11
  • 2

2 Answers2

1

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

Ext3h
  • 5,713
  • 17
  • 43
  • Thank you for your answer. You are right with everything. But the code was just an example showing my problem that exception in convert_to is not caught even by catch(...) which is what I would normally expect. – MartinD May 11 '23 at 17:44
  • What I mean even `catch (const std::overflow_error &e)` is not working and mainly `catch (...)` is not working here. – MartinD May 11 '23 at 17:56
  • Did you link `boost` as a static or as a shared library? Anything relying on dynamic type casts can behave quite broken across shared library boundaries when the compiler version only differs slightly. – Ext3h May 11 '23 at 21:08
  • Shared. Also see here that no exception is caught: https://godbolt.org/z/evoYeozx3 I am not sure if I have some wrong expectations or if this is something on boost side. – MartinD May 12 '23 at 04:34
  • Rephrased once again: My question is not how to avoid exception. I would like to catch it, to detect the problematic state, so I can react and simulate the previous versions boost behavior of returning inf. I don't want to convert to number now, that would change the current program behavior in many cases and that is not intended right now. – MartinD May 12 '23 at 04:38
  • Uh... I just understood. You have found a bug in boost. It's calling `terminate()` because it's internally throwing an exception from a method declared as `noexcept` (C++ will redirect all exceptions from such a method straight to termination). You can't catch it because the exception didn't even reach you. – Ext3h May 12 '23 at 09:13
  • Exactly. Ok, so bug in the boost, I need to report it. So I cannot proceed this way. Ok, thank you! – MartinD May 12 '23 at 10:16
0

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
gera verbun
  • 285
  • 3
  • 6
  • Even `mp::cpp_int number(strNumber);` in isolation can still throw. – Ext3h May 11 '23 at 12:40
  • Thank you for your answer. I will explore the mentioned approaches further. The thing is that that will be the next step, which will in fact change the behaviour of the existing code. My initial idea was just to catch exception and if it is there, return infinity, so that program with boost 1.79+ will quickly work exactly the same way as 1.78- without compatibility break concerns. Then the algorithms itself can be later modified and improved to return numbers instead of infitinity. But that is what now actually I don't want. I would like to detect the exception and handle it "with inifinity". – MartinD May 11 '23 at 17:48
  • Meaning existing program after upgrade of boost library dependency will still give the same answers as before upgrade (inf), not numbers, exception caught. Changing the behavior from inf to number is risky in my code and will be handled separately. – MartinD May 11 '23 at 17:59
  • And I need to assign the result to double (which I can from multiprecision::number I guess). But the question here really is: can I catch the exception from `convert_to` to simulate the old "inf" behavior? – MartinD May 11 '23 at 18:08
  • As stated in the second answer, seems to be bug in boost. Reported. Exception is thrown from noexcept method, which results in terminate. – MartinD May 12 '23 at 20:31