123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- // 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_
|