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