Search code examples
c++boost

How can I check whether a cast will result in overflow or underflow of the target type?


Is there a function in the Standard Library / Boost that will check whether a cast will result in overflow/underflow of the target type? For example:

unsigned value = static_cast<unsigned>(12.3); // this should pass (lossy but no overflow)
unsigned value = static_cast<unsigned>(1E123); // this should fail
unsigned value = static_cast<unsigned>(-2); // this should also fail

Does somethign like this exist? For the above examples the feature might have functionality similar to this:

template <typename Source, typename Target>
bool checkNoOverflow(const Source value)
{
  return value >= std::numeric_limits<Target>::lowest() && value <= std::numeric_limits<Target>::max();
}

The above is just a simplified example, which would fail on corner-cases such as this:

unsigned value = static_cast<unsigned>(-0.1); // valid, result is 0

To be clear, I'm not asking about how to write a function that would work for all cases, but rather I'm after existing functionality in the Standard Library or boost that does this for me.


Solution

  • Unfortunately, no such function exists in the C++ standard library. That leaves a few options:


    Option #1: boost::numeric_cast

    If you have access to Boost, then boost::numeric_cast should do exactly what you want. It will perform a conversion between two numeric types unless the conversion would go outside the range of the target type. In such cases, a boost::numeric::bad_numeric_cast exception is thrown.


    Option #2: gsl::narrow()

    Alternatively, gsl::narrow is a good option. I'd consider it preferable to Boost if you are OK with bringing GSL into your project.


    Option #3: Roll your own.

    If you were curious about implementing your own conversion, you can take a look at The C++ Programming Language 4th Edition by Bjarne Stroustrup (ISBN 978-0321563842).

    In Chapter 11.5, he defines a narrow_cast as such:

    template<class Target, class Source>
    Target narrow_cast(Source v)
    {
       auto r = static_cast<Target>(v); // convert the value to the target type
       if (static_cast<Source>(r)!=v)
          throw runtime_error("narrow_cast<>() failed");
       return r;
    }
    

    This implementation is quite bare and doesn't go quite as far as Boost's does, but could conceivably be modified to suit your needs.