Let\'s say I\'ve got a function that accepts a 64-bit integer, and I want to call
it with a double
with arbitrary numeric value (i.e. it may be very large in
magnit
Here's a solution that doesn't fit all the criteria, along with analysis for why not. See the accepted answer for a better answer.
// Define constants from the question.
static constexpr int64_t kint64min = std::numeric_limits::min();
static constexpr int64_t kint64max = std::numeric_limits::max();
int64_t SafeCast(double d) {
// Handle NaN specially.
if (std::isnan(d)) return 0;
// Handle out of range below.
if (d <= kint64min) return kint64min;
// Handle out of range above.
if (d >= kint64max) return kint64max;
// At this point we know that d is in range.
return d;
}
I believe this avoids undefined behavior. There is nothing to be wary of with
casting integers to doubles in the range checks. Assuming sanity in the way
that non-representable integers are converted (in particular that the mapping
is monotonic), by the time the range checks are past, we can be sure that d
is in [-2^63, 2^63)
, as required for the implicit cast at the end of the
function.
I'm also confident that this clamps out of range values correctly.
The issue is criteria #2 from the update to my question. Consider an
implementation where kint64max
is not representable as a double, but
kint64max - 1
is. Further, assume that this is an implementation where
casting kint64max
to a double yields the next lower representable value,
i.e. kint64max - 1
. Let d
be 2^63 - 2 (i.e. kint64max - 1
). Then
SafeCast(d)
is kint64max
, because the range check converts kint64max
to
a double, yielding a value equal to d
. But static_cast
is
kint64max - 1
.
Try as I might, I can't find a way to resolve this. Nor can I even write a unit test that checks my criteria, without the unit test executing undefined behavior. I feel like there is a deeper lesson to be learned here—something about the impossibility of detecting whether an action in a system will cause undefined behavior from inside the system itself, without causing undefined behavior.