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