|
Boost Users : |
Subject: Re: [Boost-users] Numeric Cast from integer to floating point
From: Antony Polukhin (antoshkka_at_[hidden])
Date: 2014-09-12 11:35:06
2014-09-08 17:53 GMT+04:00 <dariomt_at_[hidden]>:
> 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):
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
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net