| // Copyright 2017 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ | |
| #define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <climits> | |
| #include <cmath> | |
| #include <cstdlib> | |
| #include <limits> | |
| #include <type_traits> | |
| #include "base/numerics/checked_math.h" | |
| #include "base/numerics/safe_conversions.h" | |
| #include "base/numerics/safe_math_shared_impl.h" | |
| namespace base { | |
| namespace internal { | |
| template <typename T, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_signed<T>::value>::type* = nullptr> | |
| constexpr T SaturatedNegWrapper(T value) { | |
| return MustTreatAsConstexpr(value) || !ClampedNegFastOp<T>::is_supported | |
| ? (NegateWrapper(value) != std::numeric_limits<T>::lowest() | |
| ? NegateWrapper(value) | |
| : std::numeric_limits<T>::max()) | |
| : ClampedNegFastOp<T>::Do(value); | |
| } | |
| template <typename T, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| !std::is_signed<T>::value>::type* = nullptr> | |
| constexpr T SaturatedNegWrapper(T value) { | |
| return T(0); | |
| } | |
| template < | |
| typename T, | |
| typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> | |
| constexpr T SaturatedNegWrapper(T value) { | |
| return -value; | |
| } | |
| template <typename T, | |
| typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> | |
| constexpr T SaturatedAbsWrapper(T value) { | |
| // The calculation below is a static identity for unsigned types, but for | |
| // signed integer types it provides a non-branching, saturated absolute value. | |
| // This works because SafeUnsignedAbs() returns an unsigned type, which can | |
| // represent the absolute value of all negative numbers of an equal-width | |
| // integer type. The call to IsValueNegative() then detects overflow in the | |
| // special case of numeric_limits<T>::min(), by evaluating the bit pattern as | |
| // a signed integer value. If it is the overflow case, we end up subtracting | |
| // one from the unsigned result, thus saturating to numeric_limits<T>::max(). | |
| return static_cast<T>(SafeUnsignedAbs(value) - | |
| IsValueNegative<T>(SafeUnsignedAbs(value))); | |
| } | |
| template < | |
| typename T, | |
| typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> | |
| constexpr T SaturatedAbsWrapper(T value) { | |
| return value < 0 ? -value : value; | |
| } | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedAddOp {}; | |
| template <typename T, typename U> | |
| struct ClampedAddOp<T, | |
| U, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_integral<U>::value>::type> { | |
| using result_type = typename MaxExponentPromotion<T, U>::type; | |
| template <typename V = result_type> | |
| static constexpr V Do(T x, U y) { | |
| if (ClampedAddFastOp<T, U>::is_supported) | |
| return ClampedAddFastOp<T, U>::template Do<V>(x, y); | |
| static_assert(std::is_same<V, result_type>::value || | |
| IsTypeInRangeForNumericType<U, V>::value, | |
| "The saturation result cannot be determined from the " | |
| "provided types."); | |
| const V saturated = CommonMaxOrMin<V>(IsValueNegative(y)); | |
| V result = {}; | |
| return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result))) | |
| ? result | |
| : saturated; | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedSubOp {}; | |
| template <typename T, typename U> | |
| struct ClampedSubOp<T, | |
| U, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_integral<U>::value>::type> { | |
| using result_type = typename MaxExponentPromotion<T, U>::type; | |
| template <typename V = result_type> | |
| static constexpr V Do(T x, U y) { | |
| // TODO(jschuh) Make this "constexpr if" once we're C++17. | |
| if (ClampedSubFastOp<T, U>::is_supported) | |
| return ClampedSubFastOp<T, U>::template Do<V>(x, y); | |
| static_assert(std::is_same<V, result_type>::value || | |
| IsTypeInRangeForNumericType<U, V>::value, | |
| "The saturation result cannot be determined from the " | |
| "provided types."); | |
| const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y)); | |
| V result = {}; | |
| return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result))) | |
| ? result | |
| : saturated; | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedMulOp {}; | |
| template <typename T, typename U> | |
| struct ClampedMulOp<T, | |
| U, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_integral<U>::value>::type> { | |
| using result_type = typename MaxExponentPromotion<T, U>::type; | |
| template <typename V = result_type> | |
| static constexpr V Do(T x, U y) { | |
| // TODO(jschuh) Make this "constexpr if" once we're C++17. | |
| if (ClampedMulFastOp<T, U>::is_supported) | |
| return ClampedMulFastOp<T, U>::template Do<V>(x, y); | |
| V result = {}; | |
| const V saturated = | |
| CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)); | |
| return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result))) | |
| ? result | |
| : saturated; | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedDivOp {}; | |
| template <typename T, typename U> | |
| struct ClampedDivOp<T, | |
| U, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_integral<U>::value>::type> { | |
| using result_type = typename MaxExponentPromotion<T, U>::type; | |
| template <typename V = result_type> | |
| static constexpr V Do(T x, U y) { | |
| V result = {}; | |
| if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result)))) | |
| return result; | |
| // Saturation goes to max, min, or NaN (if x is zero). | |
| return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)) | |
| : SaturationDefaultLimits<V>::NaN(); | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedModOp {}; | |
| template <typename T, typename U> | |
| struct ClampedModOp<T, | |
| U, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_integral<U>::value>::type> { | |
| using result_type = typename MaxExponentPromotion<T, U>::type; | |
| template <typename V = result_type> | |
| static constexpr V Do(T x, U y) { | |
| V result = {}; | |
| return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result))) | |
| ? result | |
| : x; | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedLshOp {}; | |
| // Left shift. Non-zero values saturate in the direction of the sign. A zero | |
| // shifted by any value always results in zero. | |
| template <typename T, typename U> | |
| struct ClampedLshOp<T, | |
| U, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_integral<U>::value>::type> { | |
| using result_type = T; | |
| template <typename V = result_type> | |
| static constexpr V Do(T x, U shift) { | |
| static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); | |
| if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) { | |
| // Shift as unsigned to avoid undefined behavior. | |
| V result = static_cast<V>(as_unsigned(x) << shift); | |
| // If the shift can be reversed, we know it was valid. | |
| if (BASE_NUMERICS_LIKELY(result >> shift == x)) | |
| return result; | |
| } | |
| return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0; | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedRshOp {}; | |
| // Right shift. Negative values saturate to -1. Positive or 0 saturates to 0. | |
| template <typename T, typename U> | |
| struct ClampedRshOp<T, | |
| U, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_integral<U>::value>::type> { | |
| using result_type = T; | |
| template <typename V = result_type> | |
| static constexpr V Do(T x, U shift) { | |
| static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); | |
| // Signed right shift is odd, because it saturates to -1 or 0. | |
| const V saturated = as_unsigned(V(0)) - IsValueNegative(x); | |
| return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value) | |
| ? saturated_cast<V>(x >> shift) | |
| : saturated; | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedAndOp {}; | |
| template <typename T, typename U> | |
| struct ClampedAndOp<T, | |
| U, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_integral<U>::value>::type> { | |
| using result_type = typename std::make_unsigned< | |
| typename MaxExponentPromotion<T, U>::type>::type; | |
| template <typename V> | |
| static constexpr V Do(T x, U y) { | |
| return static_cast<result_type>(x) & static_cast<result_type>(y); | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedOrOp {}; | |
| // For simplicity we promote to unsigned integers. | |
| template <typename T, typename U> | |
| struct ClampedOrOp<T, | |
| U, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_integral<U>::value>::type> { | |
| using result_type = typename std::make_unsigned< | |
| typename MaxExponentPromotion<T, U>::type>::type; | |
| template <typename V> | |
| static constexpr V Do(T x, U y) { | |
| return static_cast<result_type>(x) | static_cast<result_type>(y); | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedXorOp {}; | |
| // For simplicity we support only unsigned integers. | |
| template <typename T, typename U> | |
| struct ClampedXorOp<T, | |
| U, | |
| typename std::enable_if<std::is_integral<T>::value && | |
| std::is_integral<U>::value>::type> { | |
| using result_type = typename std::make_unsigned< | |
| typename MaxExponentPromotion<T, U>::type>::type; | |
| template <typename V> | |
| static constexpr V Do(T x, U y) { | |
| return static_cast<result_type>(x) ^ static_cast<result_type>(y); | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedMaxOp {}; | |
| template <typename T, typename U> | |
| struct ClampedMaxOp< | |
| T, | |
| U, | |
| typename std::enable_if<std::is_arithmetic<T>::value && | |
| std::is_arithmetic<U>::value>::type> { | |
| using result_type = typename MaxExponentPromotion<T, U>::type; | |
| template <typename V = result_type> | |
| static constexpr V Do(T x, U y) { | |
| return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x) | |
| : saturated_cast<V>(y); | |
| } | |
| }; | |
| template <typename T, typename U, class Enable = void> | |
| struct ClampedMinOp {}; | |
| template <typename T, typename U> | |
| struct ClampedMinOp< | |
| T, | |
| U, | |
| typename std::enable_if<std::is_arithmetic<T>::value && | |
| std::is_arithmetic<U>::value>::type> { | |
| using result_type = typename LowestValuePromotion<T, U>::type; | |
| template <typename V = result_type> | |
| static constexpr V Do(T x, U y) { | |
| return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x) | |
| : saturated_cast<V>(y); | |
| } | |
| }; | |
| // This is just boilerplate that wraps the standard floating point arithmetic. | |
| // A macro isn't the nicest solution, but it beats rewriting these repeatedly. | |
| #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ | |
| template <typename T, typename U> \ | |
| struct Clamped##NAME##Op< \ | |
| T, U, \ | |
| typename std::enable_if<std::is_floating_point<T>::value || \ | |
| std::is_floating_point<U>::value>::type> { \ | |
| using result_type = typename MaxExponentPromotion<T, U>::type; \ | |
| template <typename V = result_type> \ | |
| static constexpr V Do(T x, U y) { \ | |
| return saturated_cast<V>(x OP y); \ | |
| } \ | |
| }; | |
| BASE_FLOAT_ARITHMETIC_OPS(Add, +) | |
| BASE_FLOAT_ARITHMETIC_OPS(Sub, -) | |
| BASE_FLOAT_ARITHMETIC_OPS(Mul, *) | |
| BASE_FLOAT_ARITHMETIC_OPS(Div, /) | |
| #undef BASE_FLOAT_ARITHMETIC_OPS | |
| } // namespace internal | |
| } // namespace base | |
| #endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ |