Is there a purpose to using boost::numeric_cast<double>(long) (i.e., converting from long to double)?

415 Views Asked by At

I thought that this conversion cannot fail. So boost::numeric_cast<double>(long) should produce the same result as just a regular cast.

Is this correct? If so, why is the boost::numeric_cast slower than a regular cast? Is there some sort of check it is doing?

3

There are 3 best solutions below

4
On

From the documentation:

The lack of preservation of range makes conversions between numeric types error prone. This is true for both implicit conversions and explicit conversions (through static_cast). numeric_cast detects loss of range when a numeric type is converted, and throws an exception if the range cannot be preserved.

So it looks like boost's numeric casts do some extra checking, and can throw exceptions -- so they're not always the same as a "regular cast".

0
On
static_assert((1ull<<57ull)!=(1+(1ull<<57ull)));
static_assert((double)(1ull<<57ull)==(double)(1+(1ull<<57ull)));

boost numeric cast would throw rather that round, as the above code does.

64 bit integers can represent some integers that 64 bit doubles cannot. 64 bit doubles spend bits on the 'exponent'.

1
On

The documentation says boost::numeric_cast is for casting without loss of range. The range of long is not necessarily narrower than double, therefore boost::numeric_cast<double>(long) may produce a different result from a regular cast. For example an implementation can have 96-bit long and 72-bit double with a very small double range. There's nothing wrong with that, it's completely C++ compliant because types in C++ don't have a fixed size and only have a minimum size. See What does the C++ standard state the size of int, long type to be?

Besides the documentation is probably a little bit unclear in that boost::numeric_cast also prevents conversion from a value not representable in the target type. For obvious reasons, a floating-point value doesn't use all of its bits for the significant part of the value and trade range for precision. Therefore the precision of an N-bit double would be smaller than N and is equal to std::numeric_limits<double>::digits/DBL_MANT_DIG digits. For IEEE-754 binary64 the precision is 53 bits, therefore if the long value requires more than 53 bits of significand then obviously it can't be stored in a double. For example 0xABCDEF9876543210 = -6066929684898893296 has a 60-bit significand, which is the distance between the first and last 1 bit. When converted to double it'll be rounded to -6066929684898892800. The change in value means boost::numeric_cast would fail. Some languages like JavaScript even have a MAX_SAFE_INTEGER constant for this

See also