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