|
Boost : |
From: Andreas Grosam (agrosam_at_[hidden])
Date: 2002-08-09 14:03:02
Hi All,
The program below shows an issue with the boost::numeric_cast operator.
Platform: Mac OS 9
Compiler: Metrowerks
Boost: v1.27, v1.28
There is an attempt to cast an unsigned type to its corresponding signed
type. Example:
unsigend long ul = ...;
signed long x = numeric_cast<signed long>(ul);
In the example the argument is set to numeric_limits<UnsignedT>::max().
Since the result is of a signed type, it can't hold this value and IMO a
bad_numeric_cast should be thrown in this case - I guess so.
Nevertheless, the program compiles and runs successfully, without throwing a
boost::bad_numeric_cast - which was expected.
I would like to know, if any other has already encountered this issue - and
whether it is already known as a bug.
I do not know, how other compilers do behave in this case - so I would appreciate
it if some others could investigate this issue on his prefered compiler, too.
A more detailed description follows.
Thanks in advance.
Here is the program:
#include <iostream>
#include <limits>
#include <boost/cast.hpp>
int main()
{
using namespace std;
try {
signed char c = boost::numeric_cast<signed char>(numeric_limits<unsigned char>::max());
cout << (int)c << endl;
signed int x = boost::numeric_cast<signed int>(numeric_limits<unsigned int>::max());
cout << x << endl;
}
catch (exception& ex)
{
cout << ex.what() << endl;
return -1;
}
return 0;
}
%OUTPUT:
-1
-1
%END OUTPUT
Discussion:
In this example - with the Metrowerks compiler-, the numeric cast is compiled as:
template<typename Target, typename Source>
inline Target numeric_cast(Source arg)
{
typedef std::numeric_limits<Source> arg_traits;
typedef detail::fixed_numeric_limits<Target> result_traits;
const bool arg_is_signed = arg_traits::is_signed;
const bool result_is_signed = result_traits::is_signed;
const bool same_sign = arg_is_signed == result_is_signed;
if (less_than_type_min<arg_is_signed, result_is_signed>::check(arg, result_traits::min())
|| greater_than_type_max<same_sign, arg_is_signed>::check(arg, result_traits::max())
)
{
throw bad_numeric_cast();
}
return static_cast<Target>(arg);
}
It turned out that the functor greater_than_type_max does not
evaluate the expected result returned by check().
If we take a closer look, greater_than_type_max has been compiled as:
struct greater_than_type_max<false, false>
{
// What does the standard say about this? I think it's right, and it
// will work with every compiler I know of.
template <class X, class Y>
static inline bool check(X x, Y)
{ return static_cast<X>(static_cast<Y>(x)) != x; }
};
That is, check() is compiled as:
bool check(X x, Y) { return static_cast<X>(static_cast<Y>(x)) != x; }
(where x is the argument)
check() can be written as:
signedT r1 = static_cast<signedT>(x);
unsigendT r2 = static_cast<unsignedT>(r1);
return r2 != x;
The Metrowerks compiler does not change any bit when making a static_cast for an
unsigend T to an signed T (at least for char, int, long, short, etc).
So the static_cast is essentially a reinterpret_cast.
Thus we can reduce the function to:
r2 = x;
return r2 != x;
or
return false;
That means, the greater_than_type_max::check() returns allways false, regardless
of the real values and whether an overflow occurs and the bad_numeric_cast will never
be thrown.
Note: the boost::numeric_cast function has conditional compiler flags.
It looks like that the second version would compile such, that it
then would evaluates the right result:
template<typename Target, typename Source>
inline Target numeric_cast(Source arg BOOST_EXPLICIT_DEFAULT_TARGET)
{
// typedefs abbreviating respective trait classes
typedef std::numeric_limits<Source> arg_traits;
typedef detail::fixed_numeric_limits<Target> result_traits;
if ((arg < 0 && !result_traits::is_signed) // loss of negative range
|| (arg_traits::is_signed && arg < result_traits::min()) // underflow
|| arg > result_traits::max()) // overflow
{
throw bad_numeric_cast();
}
return static_cast<Target>(arg);
}
This looks more reasonable to me.
Regards
Andreas Grosam
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk