OLD | NEW |
---|---|
(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 IsValueConstantAndNegative(const T v) { | |
28 #if defined(__clang__) || defined(__GNUC__) | |
29 return __builtin_constant_p(v) ? IsValueNegative(v) : false; | |
30 #else | |
31 return false; | |
32 #endif | |
33 } | |
34 | |
35 template <typename T, typename U, class Enable = void> | |
36 struct ClampedAddOp {}; | |
37 | |
38 template <typename T, typename U> | |
39 struct ClampedAddOp<T, | |
40 U, | |
41 typename std::enable_if<std::is_integral<T>::value && | |
42 std::is_integral<U>::value>::type> { | |
43 using result_type = typename MaxExponentPromotion<T, U>::type; | |
44 template <typename V = result_type> | |
45 static V Do(T x, U y) { | |
46 V result; | |
47 return CheckedAddOp<T, U>::Do(x, y, &result) | |
48 ? result | |
49 : (as_unsigned(std::numeric_limits<V>::max()) + | |
50 (IsValueConstantAndNegative(x) || IsValueNegative(y))); | |
dcheng
2017/06/30 07:39:31
This is glorious and terrible.
P.S. I think these
jschuh
2017/07/01 06:32:00
Done.
| |
51 } | |
52 }; | |
53 | |
54 template <typename T, typename U, class Enable = void> | |
55 struct ClampedSubOp {}; | |
56 | |
57 template <typename T, typename U> | |
58 struct ClampedSubOp<T, | |
59 U, | |
60 typename std::enable_if<std::is_integral<T>::value && | |
61 std::is_integral<U>::value>::type> { | |
62 using result_type = typename MaxExponentPromotion<T, U>::type; | |
63 template <typename V = result_type> | |
64 static V Do(T x, U y) { | |
65 V result; | |
66 return CheckedSubOp<T, U>::Do(x, y, &result) | |
67 ? result | |
68 : (as_unsigned(std::numeric_limits<V>::max()) + | |
69 (IsValueConstantAndNegative(x) || !IsValueNegative(y))); | |
dcheng
2017/06/30 07:39:31
Ditto.
jschuh
2017/07/01 06:31:59
Done.
| |
70 } | |
71 }; | |
72 | |
73 template <typename T, typename U, class Enable = void> | |
74 struct ClampedMulOp {}; | |
75 | |
76 template <typename T, typename U> | |
77 struct ClampedMulOp<T, | |
78 U, | |
79 typename std::enable_if<std::is_integral<T>::value && | |
80 std::is_integral<U>::value>::type> { | |
81 using result_type = typename MaxExponentPromotion<T, U>::type; | |
82 template <typename V = result_type> | |
83 static V Do(T x, U y) { | |
84 V result; | |
85 return CheckedMulOp<T, U>::Do(x, y, &result) | |
86 ? result | |
87 : as_unsigned(std::numeric_limits<V>::max()) + | |
88 (IsValueNegative(x) ^ IsValueNegative(y)); | |
89 } | |
90 }; | |
91 | |
92 template <typename T, typename U, class Enable = void> | |
93 struct ClampedDivOp {}; | |
94 | |
95 template <typename T, typename U> | |
96 struct ClampedDivOp<T, | |
97 U, | |
98 typename std::enable_if<std::is_integral<T>::value && | |
99 std::is_integral<U>::value>::type> { | |
100 using result_type = typename MaxExponentPromotion<T, U>::type; | |
101 template <typename V = result_type> | |
102 static V Do(T x, U y) { | |
103 V result; | |
104 return CheckedDivOp<T, U>::Do(x, y, &result) | |
105 ? result | |
106 : as_unsigned(std::numeric_limits<V>::max()) + | |
107 (IsValueNegative(x) ^ IsValueNegative(y)); | |
108 } | |
109 }; | |
110 | |
111 template <typename T, typename U, class Enable = void> | |
112 struct ClampedModOp {}; | |
113 | |
114 template <typename T, typename U> | |
115 struct ClampedModOp<T, | |
116 U, | |
117 typename std::enable_if<std::is_integral<T>::value && | |
118 std::is_integral<U>::value>::type> { | |
119 using result_type = typename MaxExponentPromotion<T, U>::type; | |
120 template <typename V = result_type> | |
121 static V Do(T x, U y) { | |
122 V result; | |
123 return CheckedModOp<T, U>::Do(x, y, &result) ? result : x; | |
124 } | |
125 }; | |
126 | |
127 template <typename T, typename U, class Enable = void> | |
128 struct ClampedLshOp {}; | |
129 | |
130 // Left shift. Non-zero values saturate in the direction of the sign. A zero | |
131 // shifted by any value always results in zero. | |
132 // Note: This class template supports left shifting negative values. | |
133 template <typename T, typename U> | |
134 struct ClampedLshOp<T, | |
135 U, | |
136 typename std::enable_if<std::is_integral<T>::value && | |
137 std::is_integral<U>::value>::type> { | |
138 using result_type = T; | |
139 template <typename V = result_type> | |
140 static V Do(T x, U shift) { | |
141 static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); | |
dcheng
2017/06/30 07:39:30
Oh... I guess this static assert is supposed to pr
| |
142 V result = x; | |
143 return (shift < std::numeric_limits<T>::digits && | |
144 CheckedMulOp<T, T>::Do(x, T(1) << shift, &result)) | |
145 ? result | |
146 : (x ? as_unsigned(std::numeric_limits<V>::max()) + | |
147 IsValueNegative(x) | |
148 : 0); | |
149 } | |
150 }; | |
151 | |
152 template <typename T, typename U, class Enable = void> | |
153 struct ClampedRshOp {}; | |
154 | |
155 // Right shift. Negative values saturate to -1. Positive or 0 saturates to 0. | |
156 template <typename T, typename U> | |
157 struct ClampedRshOp<T, | |
158 U, | |
159 typename std::enable_if<std::is_integral<T>::value && | |
160 std::is_integral<U>::value>::type> { | |
161 using result_type = T; | |
162 template <typename V = result_type> | |
163 static V Do(T x, U shift) { | |
164 static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); | |
165 return shift < IntegerBitsPlusSign<T>::value | |
166 ? saturated_cast<V>(x >> shift) | |
167 : as_unsigned(V(0)) - IsValueNegative(x); | |
168 } | |
169 }; | |
170 | |
171 template <typename T, typename U, class Enable = void> | |
172 struct ClampedAndOp {}; | |
173 | |
174 template <typename T, typename U> | |
175 struct ClampedAndOp<T, | |
176 U, | |
177 typename std::enable_if<std::is_integral<T>::value && | |
178 std::is_integral<U>::value>::type> { | |
179 using result_type = typename std::make_unsigned< | |
180 typename MaxExponentPromotion<T, U>::type>::type; | |
181 template <typename V> | |
182 static constexpr V Do(T x, U y) { | |
183 return static_cast<result_type>(x) & static_cast<result_type>(y); | |
184 } | |
185 }; | |
186 | |
187 template <typename T, typename U, class Enable = void> | |
188 struct ClampedOrOp {}; | |
189 | |
190 // For simplicity we promote to unsigned integers. | |
191 template <typename T, typename U> | |
192 struct ClampedOrOp<T, | |
193 U, | |
194 typename std::enable_if<std::is_integral<T>::value && | |
195 std::is_integral<U>::value>::type> { | |
196 using result_type = typename std::make_unsigned< | |
197 typename MaxExponentPromotion<T, U>::type>::type; | |
198 template <typename V> | |
199 static constexpr V Do(T x, U y) { | |
200 return static_cast<result_type>(x) | static_cast<result_type>(y); | |
201 } | |
202 }; | |
203 | |
204 template <typename T, typename U, class Enable = void> | |
205 struct ClampedXorOp {}; | |
206 | |
207 // For simplicity we support only unsigned integers. | |
208 template <typename T, typename U> | |
209 struct ClampedXorOp<T, | |
210 U, | |
211 typename std::enable_if<std::is_integral<T>::value && | |
212 std::is_integral<U>::value>::type> { | |
213 using result_type = typename std::make_unsigned< | |
214 typename MaxExponentPromotion<T, U>::type>::type; | |
215 template <typename V> | |
216 static constexpr V Do(T x, U y) { | |
217 return static_cast<result_type>(x) ^ static_cast<result_type>(y); | |
218 } | |
219 }; | |
220 | |
221 template <typename T, typename U, class Enable = void> | |
222 struct ClampedMaxOp {}; | |
223 | |
224 template <typename T, typename U> | |
225 struct ClampedMaxOp< | |
226 T, | |
227 U, | |
228 typename std::enable_if<std::is_arithmetic<T>::value && | |
229 std::is_arithmetic<U>::value>::type> { | |
230 using result_type = typename MaxExponentPromotion<T, U>::type; | |
231 template <typename V = result_type> | |
232 static constexpr V Do(T x, U y) { | |
233 return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x) | |
234 : saturated_cast<V>(y); | |
235 } | |
236 }; | |
237 | |
238 template <typename T, typename U, class Enable = void> | |
239 struct ClampedMinOp {}; | |
240 | |
241 template <typename T, typename U> | |
242 struct ClampedMinOp< | |
243 T, | |
244 U, | |
245 typename std::enable_if<std::is_arithmetic<T>::value && | |
246 std::is_arithmetic<U>::value>::type> { | |
247 using result_type = typename LowestValuePromotion<T, U>::type; | |
248 template <typename V = result_type> | |
249 static constexpr V Do(T x, U y) { | |
250 return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x) | |
251 : saturated_cast<V>(y); | |
252 } | |
253 }; | |
254 | |
255 // This is just boilerplate that wraps the standard floating point arithmetic. | |
256 // A macro isn't the nicest solution, but it beats rewriting these repeatedly. | |
257 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ | |
258 template <typename T, typename U> \ | |
259 struct Clamped##NAME##Op< \ | |
260 T, U, \ | |
261 typename std::enable_if<std::is_floating_point<T>::value || \ | |
262 std::is_floating_point<U>::value>::type> { \ | |
263 using result_type = typename MaxExponentPromotion<T, U>::type; \ | |
264 template <typename V = result_type> \ | |
265 static constexpr V Do(T x, U y) { \ | |
266 return saturated_cast<V>(x OP y); \ | |
267 } \ | |
268 }; | |
269 | |
270 BASE_FLOAT_ARITHMETIC_OPS(Add, +) | |
271 BASE_FLOAT_ARITHMETIC_OPS(Sub, -) | |
272 BASE_FLOAT_ARITHMETIC_OPS(Mul, *) | |
273 BASE_FLOAT_ARITHMETIC_OPS(Div, /) | |
274 | |
275 #undef BASE_FLOAT_ARITHMETIC_OPS | |
276 | |
277 } // namespace internal | |
278 } // namespace base | |
279 | |
280 #endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ | |
OLD | NEW |