When looking at the numeric_cast implementation, I was perplexed at some of the range checks.
For example, the test for too large value on a conversion from unsigned to signed looks like this:
template <> struct greater_than_type_max<false, false> {
template <class X, class Y>
static inline bool check(X x, Y) {
return static_cast<X>(static_cast<Y>(x)) != x;
}
};
The implementation obviously expects values to wrap if too large, so for a conversion to a smaller type, the first static_cast produces a smaller value if the range was violated. Then, the value is cast back to the original type, which would yield a different value than the original.
First of all, how safe is it to expect that all implementations wrap values? (It's not specified by the Standard, but on the other hand I've never seen any other behavior.)
Second, how could this ever work with types of the same size, say unsigned integer and integer? Assuming that the unsigned integer has a value larger than max for the integer, the first static_cast would produce a negative value. The second static_cast would then wrap again and produce the original value. What am I missing?
Onwards to a related question: What (if anything) should be done about the following behavior?
When converting floating point types to integer types, the value is truncated. In numeric_cast, the fraction isn't discarded until the return (ie the range tests are performed with the original value). This can lead to surprising behavior on edge values. Consider the following example:
char c;
double d=std::numeric_limits<char>::max();
d += .123456789123456;
c=d;
std::cout << "char value: " << (int)c << "\n";
try {
c=boost::numeric_cast<char>(d);
}
catch(boost::bad_numeric_cast& e) {
std::cout << e.what() << "\n";
}
The output on my system is:
char value: 127
bad numeric cast: loss of range in numeric_cast
The first output is correct, the second isn't.
Not very surprising - the check in numeric_cast in this case tests whether 127.123... is larger than 127, and I can't argue with the result :-). However, it is not the behavior one would expect, so I'm thinking that it a) deserves documentation or b) needs to be fixed. What do you think?
Bjorn Karlsson