16d528ed9Sopenharmony_ci// Copyright 2017 The Chromium Authors. All rights reserved.
26d528ed9Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
36d528ed9Sopenharmony_ci// found in the LICENSE file.
46d528ed9Sopenharmony_ci
56d528ed9Sopenharmony_ci#ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
66d528ed9Sopenharmony_ci#define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
76d528ed9Sopenharmony_ci
86d528ed9Sopenharmony_ci#include <stddef.h>
96d528ed9Sopenharmony_ci#include <stdint.h>
106d528ed9Sopenharmony_ci
116d528ed9Sopenharmony_ci#include <climits>
126d528ed9Sopenharmony_ci#include <cmath>
136d528ed9Sopenharmony_ci#include <cstdlib>
146d528ed9Sopenharmony_ci#include <limits>
156d528ed9Sopenharmony_ci#include <type_traits>
166d528ed9Sopenharmony_ci
176d528ed9Sopenharmony_ci#include "base/numerics/checked_math.h"
186d528ed9Sopenharmony_ci#include "base/numerics/safe_conversions.h"
196d528ed9Sopenharmony_ci#include "base/numerics/safe_math_shared_impl.h"
206d528ed9Sopenharmony_ci
216d528ed9Sopenharmony_cinamespace base {
226d528ed9Sopenharmony_cinamespace internal {
236d528ed9Sopenharmony_ci
246d528ed9Sopenharmony_citemplate <typename T,
256d528ed9Sopenharmony_ci          typename std::enable_if<std::is_integral<T>::value &&
266d528ed9Sopenharmony_ci                                  std::is_signed<T>::value>::type* = nullptr>
276d528ed9Sopenharmony_ciconstexpr T SaturatedNegWrapper(T value) {
286d528ed9Sopenharmony_ci  return MustTreatAsConstexpr(value) || !ClampedNegFastOp<T>::is_supported
296d528ed9Sopenharmony_ci             ? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
306d528ed9Sopenharmony_ci                    ? NegateWrapper(value)
316d528ed9Sopenharmony_ci                    : std::numeric_limits<T>::max())
326d528ed9Sopenharmony_ci             : ClampedNegFastOp<T>::Do(value);
336d528ed9Sopenharmony_ci}
346d528ed9Sopenharmony_ci
356d528ed9Sopenharmony_citemplate <typename T,
366d528ed9Sopenharmony_ci          typename std::enable_if<std::is_integral<T>::value &&
376d528ed9Sopenharmony_ci                                  !std::is_signed<T>::value>::type* = nullptr>
386d528ed9Sopenharmony_ciconstexpr T SaturatedNegWrapper(T value) {
396d528ed9Sopenharmony_ci  return T(0);
406d528ed9Sopenharmony_ci}
416d528ed9Sopenharmony_ci
426d528ed9Sopenharmony_citemplate <
436d528ed9Sopenharmony_ci    typename T,
446d528ed9Sopenharmony_ci    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
456d528ed9Sopenharmony_ciconstexpr T SaturatedNegWrapper(T value) {
466d528ed9Sopenharmony_ci  return -value;
476d528ed9Sopenharmony_ci}
486d528ed9Sopenharmony_ci
496d528ed9Sopenharmony_citemplate <typename T,
506d528ed9Sopenharmony_ci          typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
516d528ed9Sopenharmony_ciconstexpr T SaturatedAbsWrapper(T value) {
526d528ed9Sopenharmony_ci  // The calculation below is a static identity for unsigned types, but for
536d528ed9Sopenharmony_ci  // signed integer types it provides a non-branching, saturated absolute value.
546d528ed9Sopenharmony_ci  // This works because SafeUnsignedAbs() returns an unsigned type, which can
556d528ed9Sopenharmony_ci  // represent the absolute value of all negative numbers of an equal-width
566d528ed9Sopenharmony_ci  // integer type. The call to IsValueNegative() then detects overflow in the
576d528ed9Sopenharmony_ci  // special case of numeric_limits<T>::min(), by evaluating the bit pattern as
586d528ed9Sopenharmony_ci  // a signed integer value. If it is the overflow case, we end up subtracting
596d528ed9Sopenharmony_ci  // one from the unsigned result, thus saturating to numeric_limits<T>::max().
606d528ed9Sopenharmony_ci  return static_cast<T>(SafeUnsignedAbs(value) -
616d528ed9Sopenharmony_ci                        IsValueNegative<T>(SafeUnsignedAbs(value)));
626d528ed9Sopenharmony_ci}
636d528ed9Sopenharmony_ci
646d528ed9Sopenharmony_citemplate <
656d528ed9Sopenharmony_ci    typename T,
666d528ed9Sopenharmony_ci    typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
676d528ed9Sopenharmony_ciconstexpr T SaturatedAbsWrapper(T value) {
686d528ed9Sopenharmony_ci  return value < 0 ? -value : value;
696d528ed9Sopenharmony_ci}
706d528ed9Sopenharmony_ci
716d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
726d528ed9Sopenharmony_cistruct ClampedAddOp {};
736d528ed9Sopenharmony_ci
746d528ed9Sopenharmony_citemplate <typename T, typename U>
756d528ed9Sopenharmony_cistruct ClampedAddOp<T,
766d528ed9Sopenharmony_ci                    U,
776d528ed9Sopenharmony_ci                    typename std::enable_if<std::is_integral<T>::value &&
786d528ed9Sopenharmony_ci                                            std::is_integral<U>::value>::type> {
796d528ed9Sopenharmony_ci  using result_type = typename MaxExponentPromotion<T, U>::type;
806d528ed9Sopenharmony_ci  template <typename V = result_type>
816d528ed9Sopenharmony_ci  static constexpr V Do(T x, U y) {
826d528ed9Sopenharmony_ci    if (ClampedAddFastOp<T, U>::is_supported)
836d528ed9Sopenharmony_ci      return ClampedAddFastOp<T, U>::template Do<V>(x, y);
846d528ed9Sopenharmony_ci
856d528ed9Sopenharmony_ci    static_assert(std::is_same<V, result_type>::value ||
866d528ed9Sopenharmony_ci                      IsTypeInRangeForNumericType<U, V>::value,
876d528ed9Sopenharmony_ci                  "The saturation result cannot be determined from the "
886d528ed9Sopenharmony_ci                  "provided types.");
896d528ed9Sopenharmony_ci    const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
906d528ed9Sopenharmony_ci    V result = {};
916d528ed9Sopenharmony_ci    return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
926d528ed9Sopenharmony_ci               ? result
936d528ed9Sopenharmony_ci               : saturated;
946d528ed9Sopenharmony_ci  }
956d528ed9Sopenharmony_ci};
966d528ed9Sopenharmony_ci
976d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
986d528ed9Sopenharmony_cistruct ClampedSubOp {};
996d528ed9Sopenharmony_ci
1006d528ed9Sopenharmony_citemplate <typename T, typename U>
1016d528ed9Sopenharmony_cistruct ClampedSubOp<T,
1026d528ed9Sopenharmony_ci                    U,
1036d528ed9Sopenharmony_ci                    typename std::enable_if<std::is_integral<T>::value &&
1046d528ed9Sopenharmony_ci                                            std::is_integral<U>::value>::type> {
1056d528ed9Sopenharmony_ci  using result_type = typename MaxExponentPromotion<T, U>::type;
1066d528ed9Sopenharmony_ci  template <typename V = result_type>
1076d528ed9Sopenharmony_ci  static constexpr V Do(T x, U y) {
1086d528ed9Sopenharmony_ci    // TODO(jschuh) Make this "constexpr if" once we're C++17.
1096d528ed9Sopenharmony_ci    if (ClampedSubFastOp<T, U>::is_supported)
1106d528ed9Sopenharmony_ci      return ClampedSubFastOp<T, U>::template Do<V>(x, y);
1116d528ed9Sopenharmony_ci
1126d528ed9Sopenharmony_ci    static_assert(std::is_same<V, result_type>::value ||
1136d528ed9Sopenharmony_ci                      IsTypeInRangeForNumericType<U, V>::value,
1146d528ed9Sopenharmony_ci                  "The saturation result cannot be determined from the "
1156d528ed9Sopenharmony_ci                  "provided types.");
1166d528ed9Sopenharmony_ci    const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
1176d528ed9Sopenharmony_ci    V result = {};
1186d528ed9Sopenharmony_ci    return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
1196d528ed9Sopenharmony_ci               ? result
1206d528ed9Sopenharmony_ci               : saturated;
1216d528ed9Sopenharmony_ci  }
1226d528ed9Sopenharmony_ci};
1236d528ed9Sopenharmony_ci
1246d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
1256d528ed9Sopenharmony_cistruct ClampedMulOp {};
1266d528ed9Sopenharmony_ci
1276d528ed9Sopenharmony_citemplate <typename T, typename U>
1286d528ed9Sopenharmony_cistruct ClampedMulOp<T,
1296d528ed9Sopenharmony_ci                    U,
1306d528ed9Sopenharmony_ci                    typename std::enable_if<std::is_integral<T>::value &&
1316d528ed9Sopenharmony_ci                                            std::is_integral<U>::value>::type> {
1326d528ed9Sopenharmony_ci  using result_type = typename MaxExponentPromotion<T, U>::type;
1336d528ed9Sopenharmony_ci  template <typename V = result_type>
1346d528ed9Sopenharmony_ci  static constexpr V Do(T x, U y) {
1356d528ed9Sopenharmony_ci    // TODO(jschuh) Make this "constexpr if" once we're C++17.
1366d528ed9Sopenharmony_ci    if (ClampedMulFastOp<T, U>::is_supported)
1376d528ed9Sopenharmony_ci      return ClampedMulFastOp<T, U>::template Do<V>(x, y);
1386d528ed9Sopenharmony_ci
1396d528ed9Sopenharmony_ci    V result = {};
1406d528ed9Sopenharmony_ci    const V saturated =
1416d528ed9Sopenharmony_ci        CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
1426d528ed9Sopenharmony_ci    return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
1436d528ed9Sopenharmony_ci               ? result
1446d528ed9Sopenharmony_ci               : saturated;
1456d528ed9Sopenharmony_ci  }
1466d528ed9Sopenharmony_ci};
1476d528ed9Sopenharmony_ci
1486d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
1496d528ed9Sopenharmony_cistruct ClampedDivOp {};
1506d528ed9Sopenharmony_ci
1516d528ed9Sopenharmony_citemplate <typename T, typename U>
1526d528ed9Sopenharmony_cistruct ClampedDivOp<T,
1536d528ed9Sopenharmony_ci                    U,
1546d528ed9Sopenharmony_ci                    typename std::enable_if<std::is_integral<T>::value &&
1556d528ed9Sopenharmony_ci                                            std::is_integral<U>::value>::type> {
1566d528ed9Sopenharmony_ci  using result_type = typename MaxExponentPromotion<T, U>::type;
1576d528ed9Sopenharmony_ci  template <typename V = result_type>
1586d528ed9Sopenharmony_ci  static constexpr V Do(T x, U y) {
1596d528ed9Sopenharmony_ci    V result = {};
1606d528ed9Sopenharmony_ci    if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))
1616d528ed9Sopenharmony_ci      return result;
1626d528ed9Sopenharmony_ci    // Saturation goes to max, min, or NaN (if x is zero).
1636d528ed9Sopenharmony_ci    return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
1646d528ed9Sopenharmony_ci             : SaturationDefaultLimits<V>::NaN();
1656d528ed9Sopenharmony_ci  }
1666d528ed9Sopenharmony_ci};
1676d528ed9Sopenharmony_ci
1686d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
1696d528ed9Sopenharmony_cistruct ClampedModOp {};
1706d528ed9Sopenharmony_ci
1716d528ed9Sopenharmony_citemplate <typename T, typename U>
1726d528ed9Sopenharmony_cistruct ClampedModOp<T,
1736d528ed9Sopenharmony_ci                    U,
1746d528ed9Sopenharmony_ci                    typename std::enable_if<std::is_integral<T>::value &&
1756d528ed9Sopenharmony_ci                                            std::is_integral<U>::value>::type> {
1766d528ed9Sopenharmony_ci  using result_type = typename MaxExponentPromotion<T, U>::type;
1776d528ed9Sopenharmony_ci  template <typename V = result_type>
1786d528ed9Sopenharmony_ci  static constexpr V Do(T x, U y) {
1796d528ed9Sopenharmony_ci    V result = {};
1806d528ed9Sopenharmony_ci    return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
1816d528ed9Sopenharmony_ci               ? result
1826d528ed9Sopenharmony_ci               : x;
1836d528ed9Sopenharmony_ci  }
1846d528ed9Sopenharmony_ci};
1856d528ed9Sopenharmony_ci
1866d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
1876d528ed9Sopenharmony_cistruct ClampedLshOp {};
1886d528ed9Sopenharmony_ci
1896d528ed9Sopenharmony_ci// Left shift. Non-zero values saturate in the direction of the sign. A zero
1906d528ed9Sopenharmony_ci// shifted by any value always results in zero.
1916d528ed9Sopenharmony_citemplate <typename T, typename U>
1926d528ed9Sopenharmony_cistruct ClampedLshOp<T,
1936d528ed9Sopenharmony_ci                    U,
1946d528ed9Sopenharmony_ci                    typename std::enable_if<std::is_integral<T>::value &&
1956d528ed9Sopenharmony_ci                                            std::is_integral<U>::value>::type> {
1966d528ed9Sopenharmony_ci  using result_type = T;
1976d528ed9Sopenharmony_ci  template <typename V = result_type>
1986d528ed9Sopenharmony_ci  static constexpr V Do(T x, U shift) {
1996d528ed9Sopenharmony_ci    static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
2006d528ed9Sopenharmony_ci    if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
2016d528ed9Sopenharmony_ci      // Shift as unsigned to avoid undefined behavior.
2026d528ed9Sopenharmony_ci      V result = static_cast<V>(as_unsigned(x) << shift);
2036d528ed9Sopenharmony_ci      // If the shift can be reversed, we know it was valid.
2046d528ed9Sopenharmony_ci      if (BASE_NUMERICS_LIKELY(result >> shift == x))
2056d528ed9Sopenharmony_ci        return result;
2066d528ed9Sopenharmony_ci    }
2076d528ed9Sopenharmony_ci    return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
2086d528ed9Sopenharmony_ci  }
2096d528ed9Sopenharmony_ci};
2106d528ed9Sopenharmony_ci
2116d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
2126d528ed9Sopenharmony_cistruct ClampedRshOp {};
2136d528ed9Sopenharmony_ci
2146d528ed9Sopenharmony_ci// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
2156d528ed9Sopenharmony_citemplate <typename T, typename U>
2166d528ed9Sopenharmony_cistruct ClampedRshOp<T,
2176d528ed9Sopenharmony_ci                    U,
2186d528ed9Sopenharmony_ci                    typename std::enable_if<std::is_integral<T>::value &&
2196d528ed9Sopenharmony_ci                                            std::is_integral<U>::value>::type> {
2206d528ed9Sopenharmony_ci  using result_type = T;
2216d528ed9Sopenharmony_ci  template <typename V = result_type>
2226d528ed9Sopenharmony_ci  static constexpr V Do(T x, U shift) {
2236d528ed9Sopenharmony_ci    static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
2246d528ed9Sopenharmony_ci    // Signed right shift is odd, because it saturates to -1 or 0.
2256d528ed9Sopenharmony_ci    const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
2266d528ed9Sopenharmony_ci    return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
2276d528ed9Sopenharmony_ci               ? saturated_cast<V>(x >> shift)
2286d528ed9Sopenharmony_ci               : saturated;
2296d528ed9Sopenharmony_ci  }
2306d528ed9Sopenharmony_ci};
2316d528ed9Sopenharmony_ci
2326d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
2336d528ed9Sopenharmony_cistruct ClampedAndOp {};
2346d528ed9Sopenharmony_ci
2356d528ed9Sopenharmony_citemplate <typename T, typename U>
2366d528ed9Sopenharmony_cistruct ClampedAndOp<T,
2376d528ed9Sopenharmony_ci                    U,
2386d528ed9Sopenharmony_ci                    typename std::enable_if<std::is_integral<T>::value &&
2396d528ed9Sopenharmony_ci                                            std::is_integral<U>::value>::type> {
2406d528ed9Sopenharmony_ci  using result_type = typename std::make_unsigned<
2416d528ed9Sopenharmony_ci      typename MaxExponentPromotion<T, U>::type>::type;
2426d528ed9Sopenharmony_ci  template <typename V>
2436d528ed9Sopenharmony_ci  static constexpr V Do(T x, U y) {
2446d528ed9Sopenharmony_ci    return static_cast<result_type>(x) & static_cast<result_type>(y);
2456d528ed9Sopenharmony_ci  }
2466d528ed9Sopenharmony_ci};
2476d528ed9Sopenharmony_ci
2486d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
2496d528ed9Sopenharmony_cistruct ClampedOrOp {};
2506d528ed9Sopenharmony_ci
2516d528ed9Sopenharmony_ci// For simplicity we promote to unsigned integers.
2526d528ed9Sopenharmony_citemplate <typename T, typename U>
2536d528ed9Sopenharmony_cistruct ClampedOrOp<T,
2546d528ed9Sopenharmony_ci                   U,
2556d528ed9Sopenharmony_ci                   typename std::enable_if<std::is_integral<T>::value &&
2566d528ed9Sopenharmony_ci                                           std::is_integral<U>::value>::type> {
2576d528ed9Sopenharmony_ci  using result_type = typename std::make_unsigned<
2586d528ed9Sopenharmony_ci      typename MaxExponentPromotion<T, U>::type>::type;
2596d528ed9Sopenharmony_ci  template <typename V>
2606d528ed9Sopenharmony_ci  static constexpr V Do(T x, U y) {
2616d528ed9Sopenharmony_ci    return static_cast<result_type>(x) | static_cast<result_type>(y);
2626d528ed9Sopenharmony_ci  }
2636d528ed9Sopenharmony_ci};
2646d528ed9Sopenharmony_ci
2656d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
2666d528ed9Sopenharmony_cistruct ClampedXorOp {};
2676d528ed9Sopenharmony_ci
2686d528ed9Sopenharmony_ci// For simplicity we support only unsigned integers.
2696d528ed9Sopenharmony_citemplate <typename T, typename U>
2706d528ed9Sopenharmony_cistruct ClampedXorOp<T,
2716d528ed9Sopenharmony_ci                    U,
2726d528ed9Sopenharmony_ci                    typename std::enable_if<std::is_integral<T>::value &&
2736d528ed9Sopenharmony_ci                                            std::is_integral<U>::value>::type> {
2746d528ed9Sopenharmony_ci  using result_type = typename std::make_unsigned<
2756d528ed9Sopenharmony_ci      typename MaxExponentPromotion<T, U>::type>::type;
2766d528ed9Sopenharmony_ci  template <typename V>
2776d528ed9Sopenharmony_ci  static constexpr V Do(T x, U y) {
2786d528ed9Sopenharmony_ci    return static_cast<result_type>(x) ^ static_cast<result_type>(y);
2796d528ed9Sopenharmony_ci  }
2806d528ed9Sopenharmony_ci};
2816d528ed9Sopenharmony_ci
2826d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
2836d528ed9Sopenharmony_cistruct ClampedMaxOp {};
2846d528ed9Sopenharmony_ci
2856d528ed9Sopenharmony_citemplate <typename T, typename U>
2866d528ed9Sopenharmony_cistruct ClampedMaxOp<
2876d528ed9Sopenharmony_ci    T,
2886d528ed9Sopenharmony_ci    U,
2896d528ed9Sopenharmony_ci    typename std::enable_if<std::is_arithmetic<T>::value &&
2906d528ed9Sopenharmony_ci                            std::is_arithmetic<U>::value>::type> {
2916d528ed9Sopenharmony_ci  using result_type = typename MaxExponentPromotion<T, U>::type;
2926d528ed9Sopenharmony_ci  template <typename V = result_type>
2936d528ed9Sopenharmony_ci  static constexpr V Do(T x, U y) {
2946d528ed9Sopenharmony_ci    return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
2956d528ed9Sopenharmony_ci                                       : saturated_cast<V>(y);
2966d528ed9Sopenharmony_ci  }
2976d528ed9Sopenharmony_ci};
2986d528ed9Sopenharmony_ci
2996d528ed9Sopenharmony_citemplate <typename T, typename U, class Enable = void>
3006d528ed9Sopenharmony_cistruct ClampedMinOp {};
3016d528ed9Sopenharmony_ci
3026d528ed9Sopenharmony_citemplate <typename T, typename U>
3036d528ed9Sopenharmony_cistruct ClampedMinOp<
3046d528ed9Sopenharmony_ci    T,
3056d528ed9Sopenharmony_ci    U,
3066d528ed9Sopenharmony_ci    typename std::enable_if<std::is_arithmetic<T>::value &&
3076d528ed9Sopenharmony_ci                            std::is_arithmetic<U>::value>::type> {
3086d528ed9Sopenharmony_ci  using result_type = typename LowestValuePromotion<T, U>::type;
3096d528ed9Sopenharmony_ci  template <typename V = result_type>
3106d528ed9Sopenharmony_ci  static constexpr V Do(T x, U y) {
3116d528ed9Sopenharmony_ci    return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
3126d528ed9Sopenharmony_ci                                    : saturated_cast<V>(y);
3136d528ed9Sopenharmony_ci  }
3146d528ed9Sopenharmony_ci};
3156d528ed9Sopenharmony_ci
3166d528ed9Sopenharmony_ci// This is just boilerplate that wraps the standard floating point arithmetic.
3176d528ed9Sopenharmony_ci// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
3186d528ed9Sopenharmony_ci#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP)                              \
3196d528ed9Sopenharmony_ci  template <typename T, typename U>                                      \
3206d528ed9Sopenharmony_ci  struct Clamped##NAME##Op<                                              \
3216d528ed9Sopenharmony_ci      T, U,                                                              \
3226d528ed9Sopenharmony_ci      typename std::enable_if<std::is_floating_point<T>::value ||        \
3236d528ed9Sopenharmony_ci                              std::is_floating_point<U>::value>::type> { \
3246d528ed9Sopenharmony_ci    using result_type = typename MaxExponentPromotion<T, U>::type;       \
3256d528ed9Sopenharmony_ci    template <typename V = result_type>                                  \
3266d528ed9Sopenharmony_ci    static constexpr V Do(T x, U y) {                                    \
3276d528ed9Sopenharmony_ci      return saturated_cast<V>(x OP y);                                  \
3286d528ed9Sopenharmony_ci    }                                                                    \
3296d528ed9Sopenharmony_ci  };
3306d528ed9Sopenharmony_ci
3316d528ed9Sopenharmony_ciBASE_FLOAT_ARITHMETIC_OPS(Add, +)
3326d528ed9Sopenharmony_ciBASE_FLOAT_ARITHMETIC_OPS(Sub, -)
3336d528ed9Sopenharmony_ciBASE_FLOAT_ARITHMETIC_OPS(Mul, *)
3346d528ed9Sopenharmony_ciBASE_FLOAT_ARITHMETIC_OPS(Div, /)
3356d528ed9Sopenharmony_ci
3366d528ed9Sopenharmony_ci#undef BASE_FLOAT_ARITHMETIC_OPS
3376d528ed9Sopenharmony_ci
3386d528ed9Sopenharmony_ci}  // namespace internal
3396d528ed9Sopenharmony_ci}  // namespace base
3406d528ed9Sopenharmony_ci
3416d528ed9Sopenharmony_ci#endif  // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
342