Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(707)

Side by Side Diff: base/numerics/clamped_math_impl.h

Issue 2945433003: Add ClampedNumeric templates (Closed)
Patch Set: final Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/numerics/clamped_math.h ('k') | base/numerics/safe_conversions.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 // This provides a small optimization that generates more compact code when one
25 // of the components in an operation is a compile-time constant.
26 template <typename T>
27 constexpr bool IsCompileTimeConstant(const T v) {
28 #if defined(__clang__) || defined(__GNUC__)
29 return __builtin_constant_p(v);
30 #else
31 return false;
32 #endif
33 }
34
35 // This is a wrapper to generate return the max or min for a supplied type.
36 // If the argument is false, the returned value is the maximum. If true the
37 // returned value is the minimum.
38 template <typename T>
39 constexpr T GetMaxOrMin(bool is_min) {
40 // For both signed and unsigned math the bit pattern for minimum is really
41 // just one plus the maximum. However, we have to cast to unsigned to ensure
42 // we get well-defined overflow semantics.
43 return as_unsigned(std::numeric_limits<T>::max()) + is_min;
44 }
45
46 template <typename T, typename U, class Enable = void>
47 struct ClampedAddOp {};
48
49 template <typename T, typename U>
50 struct ClampedAddOp<T,
51 U,
52 typename std::enable_if<std::is_integral<T>::value &&
53 std::is_integral<U>::value>::type> {
54 using result_type = typename MaxExponentPromotion<T, U>::type;
55 template <typename V = result_type>
56 static V Do(T x, U y) {
57 V result;
58 return CheckedAddOp<T, U>::Do(x, y, &result)
59 ? result
60 // Prefer a compile-time constant (if we have one).
61 : GetMaxOrMin<V>(IsCompileTimeConstant(x) ? IsValueNegative(x)
62 : IsValueNegative(y));
63 }
64 };
65
66 template <typename T, typename U, class Enable = void>
67 struct ClampedSubOp {};
68
69 template <typename T, typename U>
70 struct ClampedSubOp<T,
71 U,
72 typename std::enable_if<std::is_integral<T>::value &&
73 std::is_integral<U>::value>::type> {
74 using result_type = typename MaxExponentPromotion<T, U>::type;
75 template <typename V = result_type>
76 static V Do(T x, U y) {
77 V result;
78 return CheckedSubOp<T, U>::Do(x, y, &result)
79 ? result
80 // Prefer a compile-time constant (if we have one).
81 : GetMaxOrMin<V>(IsCompileTimeConstant(x) ? IsValueNegative(x)
82 : !IsValueNegative(y));
83 }
84 };
85
86 template <typename T, typename U, class Enable = void>
87 struct ClampedMulOp {};
88
89 template <typename T, typename U>
90 struct ClampedMulOp<T,
91 U,
92 typename std::enable_if<std::is_integral<T>::value &&
93 std::is_integral<U>::value>::type> {
94 using result_type = typename MaxExponentPromotion<T, U>::type;
95 template <typename V = result_type>
96 static V Do(T x, U y) {
97 V result;
98 return CheckedMulOp<T, U>::Do(x, y, &result)
99 ? result
100 : GetMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
101 }
102 };
103
104 template <typename T, typename U, class Enable = void>
105 struct ClampedDivOp {};
106
107 template <typename T, typename U>
108 struct ClampedDivOp<T,
109 U,
110 typename std::enable_if<std::is_integral<T>::value &&
111 std::is_integral<U>::value>::type> {
112 using result_type = typename MaxExponentPromotion<T, U>::type;
113 template <typename V = result_type>
114 static V Do(T x, U y) {
115 V result = SaturationDefaultLimits<V>::NaN();
116 return !x || CheckedDivOp<T, U>::Do(x, y, &result)
117 ? result
118 : GetMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
119 }
120 };
121
122 template <typename T, typename U, class Enable = void>
123 struct ClampedModOp {};
124
125 template <typename T, typename U>
126 struct ClampedModOp<T,
127 U,
128 typename std::enable_if<std::is_integral<T>::value &&
129 std::is_integral<U>::value>::type> {
130 using result_type = typename MaxExponentPromotion<T, U>::type;
131 template <typename V = result_type>
132 static V Do(T x, U y) {
133 V result;
134 return CheckedModOp<T, U>::Do(x, y, &result) ? result : x;
135 }
136 };
137
138 template <typename T, typename U, class Enable = void>
139 struct ClampedLshOp {};
140
141 // Left shift. Non-zero values saturate in the direction of the sign. A zero
142 // shifted by any value always results in zero.
143 // Note: This class template supports left shifting negative values.
144 template <typename T, typename U>
145 struct ClampedLshOp<T,
146 U,
147 typename std::enable_if<std::is_integral<T>::value &&
148 std::is_integral<U>::value>::type> {
149 using result_type = T;
150 template <typename V = result_type>
151 static V Do(T x, U shift) {
152 static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
153 V result = x;
154 return (shift < std::numeric_limits<T>::digits &&
155 CheckedMulOp<T, T>::Do(x, T(1) << shift, &result))
156 ? result
157 : (x ? GetMaxOrMin<V>(IsValueNegative(x)) : 0);
158 }
159 };
160
161 template <typename T, typename U, class Enable = void>
162 struct ClampedRshOp {};
163
164 // Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
165 template <typename T, typename U>
166 struct ClampedRshOp<T,
167 U,
168 typename std::enable_if<std::is_integral<T>::value &&
169 std::is_integral<U>::value>::type> {
170 using result_type = T;
171 template <typename V = result_type>
172 static V Do(T x, U shift) {
173 static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
174 return shift < IntegerBitsPlusSign<T>::value
175 ? saturated_cast<V>(x >> shift)
176 // Signed right shift is odd, because it saturates to -1 or 0.
177 : as_unsigned(V(0)) - IsValueNegative(x);
178 }
179 };
180
181 template <typename T, typename U, class Enable = void>
182 struct ClampedAndOp {};
183
184 template <typename T, typename U>
185 struct ClampedAndOp<T,
186 U,
187 typename std::enable_if<std::is_integral<T>::value &&
188 std::is_integral<U>::value>::type> {
189 using result_type = typename std::make_unsigned<
190 typename MaxExponentPromotion<T, U>::type>::type;
191 template <typename V>
192 static constexpr V Do(T x, U y) {
193 return static_cast<result_type>(x) & static_cast<result_type>(y);
194 }
195 };
196
197 template <typename T, typename U, class Enable = void>
198 struct ClampedOrOp {};
199
200 // For simplicity we promote to unsigned integers.
201 template <typename T, typename U>
202 struct ClampedOrOp<T,
203 U,
204 typename std::enable_if<std::is_integral<T>::value &&
205 std::is_integral<U>::value>::type> {
206 using result_type = typename std::make_unsigned<
207 typename MaxExponentPromotion<T, U>::type>::type;
208 template <typename V>
209 static constexpr V Do(T x, U y) {
210 return static_cast<result_type>(x) | static_cast<result_type>(y);
211 }
212 };
213
214 template <typename T, typename U, class Enable = void>
215 struct ClampedXorOp {};
216
217 // For simplicity we support only unsigned integers.
218 template <typename T, typename U>
219 struct ClampedXorOp<T,
220 U,
221 typename std::enable_if<std::is_integral<T>::value &&
222 std::is_integral<U>::value>::type> {
223 using result_type = typename std::make_unsigned<
224 typename MaxExponentPromotion<T, U>::type>::type;
225 template <typename V>
226 static constexpr V Do(T x, U y) {
227 return static_cast<result_type>(x) ^ static_cast<result_type>(y);
228 }
229 };
230
231 template <typename T, typename U, class Enable = void>
232 struct ClampedMaxOp {};
233
234 template <typename T, typename U>
235 struct ClampedMaxOp<
236 T,
237 U,
238 typename std::enable_if<std::is_arithmetic<T>::value &&
239 std::is_arithmetic<U>::value>::type> {
240 using result_type = typename MaxExponentPromotion<T, U>::type;
241 template <typename V = result_type>
242 static constexpr V Do(T x, U y) {
243 return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
244 : saturated_cast<V>(y);
245 }
246 };
247
248 template <typename T, typename U, class Enable = void>
249 struct ClampedMinOp {};
250
251 template <typename T, typename U>
252 struct ClampedMinOp<
253 T,
254 U,
255 typename std::enable_if<std::is_arithmetic<T>::value &&
256 std::is_arithmetic<U>::value>::type> {
257 using result_type = typename LowestValuePromotion<T, U>::type;
258 template <typename V = result_type>
259 static constexpr V Do(T x, U y) {
260 return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
261 : saturated_cast<V>(y);
262 }
263 };
264
265 // This is just boilerplate that wraps the standard floating point arithmetic.
266 // A macro isn't the nicest solution, but it beats rewriting these repeatedly.
267 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
268 template <typename T, typename U> \
269 struct Clamped##NAME##Op< \
270 T, U, \
271 typename std::enable_if<std::is_floating_point<T>::value || \
272 std::is_floating_point<U>::value>::type> { \
273 using result_type = typename MaxExponentPromotion<T, U>::type; \
274 template <typename V = result_type> \
275 static constexpr V Do(T x, U y) { \
276 return saturated_cast<V>(x OP y); \
277 } \
278 };
279
280 BASE_FLOAT_ARITHMETIC_OPS(Add, +)
281 BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
282 BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
283 BASE_FLOAT_ARITHMETIC_OPS(Div, /)
284
285 #undef BASE_FLOAT_ARITHMETIC_OPS
286
287 } // namespace internal
288 } // namespace base
289
290 #endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
OLDNEW
« no previous file with comments | « base/numerics/clamped_math.h ('k') | base/numerics/safe_conversions.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698