2014-09-08 17:53 GMT+04:00 <dariomt@gmail.com>:
Hi,

Quoting the docs for boost::numeric_cast: "There are several situations where conversions are unsafe: [...] Conversions from floating point types to integral types."

What about conversions from integral types to floating point types? E.g. from 64bit int to double.

The following example shows what I mean:

#include <iostream>
#include <cmath>
#include <boost/numeric/conversion/cast.hpp>

int main()
{
    const uint64_t i = 123445678911188878;
    std::cout << "i=" << i << std::endl;
    
    const double d = i;
    std::cout << "d="    << std::fixed << d << std::endl;
    std::cout << "next=" << std::fixed << std::nextafter(d,  std::numeric_limits<double>::max() ) << std::endl;
    std::cout << "prev=" << std::fixed << std::nextafter(d, -std::numeric_limits<double>::max()) << std::endl;
    
    // I'd expect the following cast to fail
    const double dd = boost::numeric_cast<double>(i);
    std::cout <<"dd=" << std::fixed << dd << std::endl;

    return 0;
}

prints
i=123445678911188878
d=123445678911188880.000000
next=123445678911188896.000000
prev=123445678911188864.000000
dd=123445678911188880.000000
because that integer cannot be represented in double precision

Is there something in Boost to help here?

That's an interesting question. numeric_cast function was designed to detect positive and negative overflows, not precision loss. Consider the example:

    std::cout << boost::numeric_cast<int>(1.1999); // OK: 1
    std::cout << boost::numeric_cast<int>(1e100); // exception: positive overflow

Your example is related to the first line, where 1.1999 is converted to int. When uint64_t i = 123445678911188878; is converted to double there is no positive/negative overflow, there's only a precision loss.

There is no out-of-the-box solution for your problem, but you can try to so something by your own using the numeric converter (code was not tested):

// See: http://www.boost.org/doc/libs/1_56_0/libs/numeric/conversion/doc/html/boost_numericconversion/converter___function_object.html#boost_numericconversion.converter___function_object.range_checking_logic

template<class Traits>
struct precise_converter
{
typedef typename Traits::result_type result_type ;
typedef typename Traits::argument_type argument_type ;

static result_type low_level_convert ( argument_type s ) {
     const result_type res = static_cast<result_type>(s) ;
     if (std::abs(s - res) >= 1) { throw std::runtime_error("precision loss"); }
     return res;
}
} ;


    template <typename Target, typename Source> 
    inline Target precise_numeric_cast( Source arg ) // use this instead of numeric_cast
    {
        typedef numeric::conversion_traits<Target, Source>   conv_traits;
        typedef numeric::numeric_cast_traits<Target, Source> cast_traits;
        typedef boost::numeric::converter
            <
                Target,
                Source, 
                conv_traits,
                typename cast_traits::overflow_policy, 
                typename cast_traits::rounding_policy, 
/* only the following line differs from the original numeric_cast*/ precise_converter< conv_traits >, typename cast_traits::range_checking_policy > converter; return converter::convert(arg); }


--
Best regards,
Antony Polukhin