Chromium Code Reviews| Index: base/numerics/safe_conversions_impl.h |
| diff --git a/base/numerics/safe_conversions_impl.h b/base/numerics/safe_conversions_impl.h |
| index c4d96365ff4d71d96ceffa739fc02dccff868c3f..5ef3321474e5d2ac83777dc1f3210cf7f0af59ad 100644 |
| --- a/base/numerics/safe_conversions_impl.h |
| +++ b/base/numerics/safe_conversions_impl.h |
| @@ -50,6 +50,36 @@ constexpr T BinaryComplement(T x) { |
| return static_cast<T>(~x); |
| } |
| +// Determines if a numeric value is negative without throwing compiler |
| +// warnings on: unsigned(value) < 0. |
| +template <typename T, |
| + typename std::enable_if<std::is_signed<T>::value>::type* = nullptr> |
| +constexpr bool IsValueNegative(T value) { |
| + static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); |
| + return value < 0; |
| +} |
| + |
| +template <typename T, |
| + typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr> |
| +constexpr bool IsValueNegative(T) { |
| + static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); |
| + return false; |
| +} |
| + |
| +// This performs a fast negation, returning a signed value. It works on unsigned |
| +// arguments, but probably doesn't do what you want for any unsigned value |
| +// larger than max / 2 + 1 (i.e. signed min cast to unsigned). |
| +template <typename T> |
| +constexpr typename std::make_signed<T>::type ConditionalNegate( |
| + T x, |
| + bool is_negative) { |
| + static_assert(std::is_integral<T>::value, "Type must be integral"); |
| + using SignedT = typename std::make_signed<T>::type; |
| + using UnsignedT = typename std::make_unsigned<T>::type; |
| + return static_cast<SignedT>( |
| + (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative); |
| +} |
| + |
| // This performs a safe, non-branching absolute value via unsigned overflow. |
| template <typename T> |
| constexpr T SafeUnsignedAbsImpl(T value, T sign_mask) { |
| @@ -181,28 +211,44 @@ constexpr inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, |
| // To fix this bug we manually truncate the maximum value when the destination |
| // type is an integral of larger precision than the source floating-point type, |
| // such that the resulting maximum is represented exactly as a floating point. |
| -template <typename Dst, typename Src> |
| +template <typename Dst, |
| + typename Src, |
| + template <typename> class Bounds = std::numeric_limits> |
| struct NarrowingRange { |
| using SrcLimits = typename std::numeric_limits<Src>; |
| using DstLimits = typename std::numeric_limits<Dst>; |
| - // The following logic avoids warnings where the max function is |
| - // instantiated with invalid values for a bit shift (even though |
| - // such a function can never be called). |
| - static const int shift = (MaxExponent<Src>::value > MaxExponent<Dst>::value && |
| - SrcLimits::digits < DstLimits::digits && |
| - SrcLimits::is_iec559 && |
| - DstLimits::is_integer) |
| - ? (DstLimits::digits - SrcLimits::digits) |
| - : 0; |
| - |
| - static constexpr Dst max() { |
| - // We use UINTMAX_C below to avoid compiler warnings about shifting floating |
| - // points. Since it's a compile time calculation, it shouldn't have any |
| - // performance impact. |
| - return DstLimits::max() - static_cast<Dst>((UINTMAX_C(1) << shift) - 1); |
| + |
| + // Computes the mask required to make an accurate comparison between types. |
| + static const int kShift = |
| + (MaxExponent<Src>::value > MaxExponent<Dst>::value && |
| + SrcLimits::digits < DstLimits::digits) |
| + ? (DstLimits::digits - SrcLimits::digits) |
| + : 0; |
| + template < |
| + typename T, |
| + typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> |
| + |
| + // Masks out the integer bits that are beyond the precision of the |
| + // intermediate type used for comparison. |
| + static constexpr T Adjust(T value) { |
| + static_assert(std::is_same<T, Dst>::value, ""); |
| + static_assert(kShift < DstLimits::digits, ""); |
| + return static_cast<T>( |
| + ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)), |
|
eae
2016/12/15 23:07:46
Nice!
|
| + IsValueNegative(value))); |
| + } |
| + |
| + template <typename T, |
| + typename std::enable_if<std::is_floating_point<T>::value>::type* = |
| + nullptr> |
| + static constexpr T Adjust(T value) { |
| + static_assert(std::is_same<T, Dst>::value, ""); |
| + static_assert(kShift == 0, ""); |
| + return value; |
| } |
| - static constexpr Dst lowest() { return DstLimits::lowest(); } |
| + static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } |
| + static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } |
| }; |
| template <typename Dst, |