1 // Copyright 2017 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ 6 #define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <climits> 12 #include <cmath> 13 #include <concepts> 14 #include <cstdlib> 15 #include <limits> 16 #include <type_traits> 17 18 #include "base/numerics/checked_math.h" 19 #include "base/numerics/safe_conversions.h" 20 #include "base/numerics/safe_math_shared_impl.h" 21 22 namespace base { 23 namespace internal { 24 25 template <typename T> requires(std::signed_integral<T>)26 requires(std::signed_integral<T>) 27 constexpr T SaturatedNegWrapper(T value) { 28 return IsConstantEvaluated() || !ClampedNegFastOp<T>::is_supported 29 ? (NegateWrapper(value) != std::numeric_limits<T>::lowest() 30 ? NegateWrapper(value) 31 : std::numeric_limits<T>::max()) 32 : ClampedNegFastOp<T>::Do(value); 33 } 34 35 template <typename T> requires(std::unsigned_integral<T>)36 requires(std::unsigned_integral<T>) 37 constexpr T SaturatedNegWrapper(T value) { 38 return T(0); 39 } 40 41 template <typename T> requires(std::floating_point<T>)42 requires(std::floating_point<T>) 43 constexpr T SaturatedNegWrapper(T value) { 44 return -value; 45 } 46 47 template <typename T> requires(std::integral<T>)48 requires(std::integral<T>) 49 constexpr T SaturatedAbsWrapper(T value) { 50 // The calculation below is a static identity for unsigned types, but for 51 // signed integer types it provides a non-branching, saturated absolute value. 52 // This works because SafeUnsignedAbs() returns an unsigned type, which can 53 // represent the absolute value of all negative numbers of an equal-width 54 // integer type. The call to IsValueNegative() then detects overflow in the 55 // special case of numeric_limits<T>::min(), by evaluating the bit pattern as 56 // a signed integer value. If it is the overflow case, we end up subtracting 57 // one from the unsigned result, thus saturating to numeric_limits<T>::max(). 58 return static_cast<T>( 59 SafeUnsignedAbs(value) - 60 IsValueNegative<T>(static_cast<T>(SafeUnsignedAbs(value)))); 61 } 62 63 template <typename T> requires(std::floating_point<T>)64 requires(std::floating_point<T>) 65 constexpr T SaturatedAbsWrapper(T value) { 66 return value < 0 ? -value : value; 67 } 68 69 template <typename T, typename U> 70 struct ClampedAddOp {}; 71 72 template <typename T, typename U> 73 requires(std::integral<T> && std::integral<U>) 74 struct ClampedAddOp<T, U> { 75 using result_type = typename MaxExponentPromotion<T, U>::type; 76 template <typename V = result_type> 77 static constexpr V Do(T x, U y) { 78 if (!IsConstantEvaluated() && ClampedAddFastOp<T, U>::is_supported) 79 return ClampedAddFastOp<T, U>::template Do<V>(x, y); 80 81 static_assert(std::is_same_v<V, result_type> || 82 IsTypeInRangeForNumericType<U, V>::value, 83 "The saturation result cannot be determined from the " 84 "provided types."); 85 const V saturated = CommonMaxOrMin<V>(IsValueNegative(y)); 86 V result = {}; 87 return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result))) 88 ? result 89 : saturated; 90 } 91 }; 92 93 template <typename T, typename U> 94 struct ClampedSubOp {}; 95 96 template <typename T, typename U> 97 requires(std::integral<T> && std::integral<U>) 98 struct ClampedSubOp<T, U> { 99 using result_type = typename MaxExponentPromotion<T, U>::type; 100 template <typename V = result_type> 101 static constexpr V Do(T x, U y) { 102 if (!IsConstantEvaluated() && ClampedSubFastOp<T, U>::is_supported) 103 return ClampedSubFastOp<T, U>::template Do<V>(x, y); 104 105 static_assert(std::is_same_v<V, result_type> || 106 IsTypeInRangeForNumericType<U, V>::value, 107 "The saturation result cannot be determined from the " 108 "provided types."); 109 const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y)); 110 V result = {}; 111 return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result))) 112 ? result 113 : saturated; 114 } 115 }; 116 117 template <typename T, typename U> 118 struct ClampedMulOp {}; 119 120 template <typename T, typename U> 121 requires(std::integral<T> && std::integral<U>) 122 struct ClampedMulOp<T, U> { 123 using result_type = typename MaxExponentPromotion<T, U>::type; 124 template <typename V = result_type> 125 static constexpr V Do(T x, U y) { 126 if (!IsConstantEvaluated() && ClampedMulFastOp<T, U>::is_supported) 127 return ClampedMulFastOp<T, U>::template Do<V>(x, y); 128 129 V result = {}; 130 const V saturated = 131 CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)); 132 return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result))) 133 ? result 134 : saturated; 135 } 136 }; 137 138 template <typename T, typename U> 139 struct ClampedDivOp {}; 140 141 template <typename T, typename U> 142 requires(std::integral<T> && std::integral<U>) 143 struct ClampedDivOp<T, U> { 144 using result_type = typename MaxExponentPromotion<T, U>::type; 145 template <typename V = result_type> 146 static constexpr V Do(T x, U y) { 147 V result = {}; 148 if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result)))) 149 return result; 150 // Saturation goes to max, min, or NaN (if x is zero). 151 return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)) 152 : SaturationDefaultLimits<V>::NaN(); 153 } 154 }; 155 156 template <typename T, typename U> 157 struct ClampedModOp {}; 158 159 template <typename T, typename U> 160 requires(std::integral<T> && std::integral<U>) 161 struct ClampedModOp<T, U> { 162 using result_type = typename MaxExponentPromotion<T, U>::type; 163 template <typename V = result_type> 164 static constexpr V Do(T x, U y) { 165 V result = {}; 166 return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result))) 167 ? result 168 : x; 169 } 170 }; 171 172 template <typename T, typename U> 173 struct ClampedLshOp {}; 174 175 // Left shift. Non-zero values saturate in the direction of the sign. A zero 176 // shifted by any value always results in zero. 177 template <typename T, typename U> 178 requires(std::integral<T> && std::integral<U>) 179 struct ClampedLshOp<T, U> { 180 using result_type = T; 181 template <typename V = result_type> 182 static constexpr V Do(T x, U shift) { 183 static_assert(!std::is_signed_v<U>, "Shift value must be unsigned."); 184 if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) { 185 // Shift as unsigned to avoid undefined behavior. 186 V result = static_cast<V>(as_unsigned(x) << shift); 187 // If the shift can be reversed, we know it was valid. 188 if (BASE_NUMERICS_LIKELY(result >> shift == x)) 189 return result; 190 } 191 return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0; 192 } 193 }; 194 195 template <typename T, typename U> 196 struct ClampedRshOp {}; 197 198 // Right shift. Negative values saturate to -1. Positive or 0 saturates to 0. 199 template <typename T, typename U> 200 requires(std::integral<T> && std::integral<U>) 201 struct ClampedRshOp<T, U> { 202 using result_type = T; 203 template <typename V = result_type> 204 static constexpr V Do(T x, U shift) { 205 static_assert(!std::is_signed_v<U>, "Shift value must be unsigned."); 206 // Signed right shift is odd, because it saturates to -1 or 0. 207 const V saturated = as_unsigned(V(0)) - IsValueNegative(x); 208 return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value) 209 ? saturated_cast<V>(x >> shift) 210 : saturated; 211 } 212 }; 213 214 template <typename T, typename U> 215 struct ClampedAndOp {}; 216 217 template <typename T, typename U> 218 requires(std::integral<T> && std::integral<U>) 219 struct ClampedAndOp<T, U> { 220 using result_type = typename std::make_unsigned< 221 typename MaxExponentPromotion<T, U>::type>::type; 222 template <typename V> 223 static constexpr V Do(T x, U y) { 224 return static_cast<result_type>(x) & static_cast<result_type>(y); 225 } 226 }; 227 228 template <typename T, typename U> 229 struct ClampedOrOp {}; 230 231 // For simplicity we promote to unsigned integers. 232 template <typename T, typename U> 233 requires(std::integral<T> && std::integral<U>) 234 struct ClampedOrOp<T, U> { 235 using result_type = typename std::make_unsigned< 236 typename MaxExponentPromotion<T, U>::type>::type; 237 template <typename V> 238 static constexpr V Do(T x, U y) { 239 return static_cast<result_type>(x) | static_cast<result_type>(y); 240 } 241 }; 242 243 template <typename T, typename U> 244 struct ClampedXorOp {}; 245 246 // For simplicity we support only unsigned integers. 247 template <typename T, typename U> 248 requires(std::integral<T> && std::integral<U>) 249 struct ClampedXorOp<T, U> { 250 using result_type = typename std::make_unsigned< 251 typename MaxExponentPromotion<T, U>::type>::type; 252 template <typename V> 253 static constexpr V Do(T x, U y) { 254 return static_cast<result_type>(x) ^ static_cast<result_type>(y); 255 } 256 }; 257 258 template <typename T, typename U> 259 struct ClampedMaxOp {}; 260 261 template <typename T, typename U> 262 requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<U>) 263 struct ClampedMaxOp<T, U> { 264 using result_type = typename MaxExponentPromotion<T, U>::type; 265 template <typename V = result_type> 266 static constexpr V Do(T x, U y) { 267 return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x) 268 : saturated_cast<V>(y); 269 } 270 }; 271 272 template <typename T, typename U> 273 struct ClampedMinOp {}; 274 275 template <typename T, typename U> 276 requires(std::is_arithmetic_v<T> && std::is_arithmetic_v<U>) 277 struct ClampedMinOp<T, U> { 278 using result_type = typename LowestValuePromotion<T, U>::type; 279 template <typename V = result_type> 280 static constexpr V Do(T x, U y) { 281 return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x) 282 : saturated_cast<V>(y); 283 } 284 }; 285 286 // This is just boilerplate that wraps the standard floating point arithmetic. 287 // A macro isn't the nicest solution, but it beats rewriting these repeatedly. 288 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ 289 template <typename T, typename U> \ 290 requires(std::floating_point<T> || std::floating_point<U>) \ 291 struct Clamped##NAME##Op<T, U> { \ 292 using result_type = typename MaxExponentPromotion<T, U>::type; \ 293 template <typename V = result_type> \ 294 static constexpr V Do(T x, U y) { \ 295 return saturated_cast<V>(x OP y); \ 296 } \ 297 }; 298 299 BASE_FLOAT_ARITHMETIC_OPS(Add, +) 300 BASE_FLOAT_ARITHMETIC_OPS(Sub, -) 301 BASE_FLOAT_ARITHMETIC_OPS(Mul, *) 302 BASE_FLOAT_ARITHMETIC_OPS(Div, /) 303 304 #undef BASE_FLOAT_ARITHMETIC_OPS 305 306 } // namespace internal 307 } // namespace base 308 309 #endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ 310