Commit 8b34cfe3 authored by jschuh's avatar jschuh Committed by Commit bot

Improve saturated_cast performance

Plumb custom bounds through DstRangeRelationToSrcRange to remove
duplicate comparisons. Refactor RangeConstraint from an enum to a class
containing bools, which can be independently evaluated at compile-time.

NOTRY=true

Review-Url: https://codereview.chromium.org/2585043002
Cr-Commit-Position: refs/heads/master@{#439508}
parent ef60bcc9
......@@ -106,6 +106,26 @@ struct SaturatedCastDefaultHandler {
};
namespace internal {
// saturated_cast<> is analogous to static_cast<> for numeric types, except
// that the specified numeric conversion will saturate by default rather than
// overflow or underflow, and NaN assignment to an integral will return 0.
// All boundary condition behaviors can be overriden with a custom handler.
template <template <typename>
class SaturationHandler = SaturatedCastDefaultHandler,
typename Dst,
typename Src>
constexpr Dst saturated_cast_impl(const Src value,
const RangeConstraint constraint) {
return constraint.IsValid()
? static_cast<Dst>(value)
: (constraint.IsOverflow()
? SaturationHandler<Dst>::HandleOverflow()
// Skip this check for integral Src, which cannot be NaN.
: (std::is_integral<Src>::value || constraint.IsUnderflow()
? SaturationHandler<Dst>::HandleUnderflow()
: SaturationHandler<Dst>::HandleNaN()));
}
// saturated_cast<> is analogous to static_cast<> for numeric types, except
// that the specified numeric conversion will saturate by default rather than
// overflow or underflow, and NaN assignment to an integral will return 0.
......@@ -115,30 +135,10 @@ template <typename Dst,
class SaturationHandler = SaturatedCastDefaultHandler,
typename Src>
constexpr Dst saturated_cast(Src value) {
static_assert(
SaturationHandler<Dst>::lowest() < SaturationHandler<Dst>::max(), "");
// While this looks like a lot of code, it's all constexpr and all but
// one variable are compile-time constants (enforced by a static_assert).
// So, it should evaluate to the minimum number of comparisons required
// for the range check, which is 0-3, depending on the exact source and
// destination types, and whatever custom range is specified.
using SrcType = typename UnderlyingType<Src>::type;
return IsGreaterOrEqual<SrcType, Dst>::Test(
value, NarrowingRange<Dst, SrcType, SaturationHandler>::lowest())
? (IsLessOrEqual<SrcType, Dst>::Test(
value,
NarrowingRange<Dst, SrcType, SaturationHandler>::max())
? static_cast<Dst>(value)
: SaturationHandler<Dst>::HandleOverflow())
// This last branch is a little confusing. It's specifically to
// catch NaN when converting from float to integral.
: (std::is_integral<SrcType>::value ||
std::is_floating_point<Dst>::value ||
IsLessOrEqual<SrcType, Dst>::Test(
value, NarrowingRange<Dst, SrcType,
SaturationHandler>::max())
? SaturationHandler<Dst>::HandleUnderflow()
: SaturationHandler<Dst>::HandleNaN());
return saturated_cast_impl<SaturationHandler, Dst>(
static_cast<SrcType>(value),
DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value));
}
// strict_cast<> is analogous to static_cast<> for numeric types, except that
......
This diff is collapsed.
......@@ -671,7 +671,14 @@ struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> {
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
// Additional saturation tests.
EXPECT_EQ(DstLimits::max(), saturated_cast<Dst>(SrcLimits::max()));
EXPECT_EQ(DstLimits::lowest(), saturated_cast<Dst>(SrcLimits::lowest()));
if (SrcLimits::is_iec559) {
EXPECT_EQ(Dst(0), saturated_cast<Dst>(SrcLimits::quiet_NaN()));
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1);
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
......@@ -714,6 +721,10 @@ struct TestNumericConversion<Dst, Src, UNSIGN_TO_SIGN_NARROW_OR_EQUAL> {
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
// Additional saturation tests.
EXPECT_EQ(DstLimits::max(), saturated_cast<Dst>(SrcLimits::max()));
EXPECT_EQ(Dst(0), saturated_cast<Dst>(SrcLimits::lowest()));
}
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment