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

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

Issue 2502083002: Restructure ArithmeticPromotion to support alternate promotions (Closed)
Patch Set: nit Created 4 years, 1 month 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
« base/numerics/safe_math.h ('K') | « base/numerics/safe_math.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ 5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_
6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_ 6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_
7 7
8 #include <stddef.h> 8 #include <stddef.h>
9 #include <stdint.h> 9 #include <stdint.h>
10 10
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> 120 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
121 PositionOfSignBit<T>::value); 121 PositionOfSignBit<T>::value);
122 } 122 }
123 123
124 // This wrapper undoes the standard integer promotions. 124 // This wrapper undoes the standard integer promotions.
125 template <typename T> 125 template <typename T>
126 constexpr T BinaryComplement(T x) { 126 constexpr T BinaryComplement(T x) {
127 return static_cast<T>(~x); 127 return static_cast<T>(~x);
128 } 128 }
129 129
130 // For integers less than 128-bit and floats 32-bit or larger, we have the type 130 enum ArithmeticPromotionCategory {
131 // with the larger maximum exponent take precedence. 131 LEFT_PROMOTION, // Use the type of the left-hand argument.
Tom Sepez 2016/11/15 22:32:13 Do we ever do left/right promotion? Or is this fo
jschuh 2016/11/15 22:38:49 Just for the forthcoming shift.
132 enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION }; 132 RIGHT_PROMOTION, // Use the type of the right-hand argument.
133 MAX_EXPONENT_PROMOTION, // Use the type supporting the largest exponent.
134 BIG_ENOUGH_PROMOTION // Attempt to find a big enough type.
135 };
136
137 template <ArithmeticPromotionCategory Promotion,
138 typename Lhs,
139 typename Rhs = Lhs>
140 struct ArithmeticPromotion;
133 141
134 template <typename Lhs, 142 template <typename Lhs,
135 typename Rhs = Lhs, 143 typename Rhs,
136 ArithmeticPromotionCategory Promotion = 144 ArithmeticPromotionCategory Promotion =
137 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) 145 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
138 ? LEFT_PROMOTION 146 ? LEFT_PROMOTION
139 : RIGHT_PROMOTION> 147 : RIGHT_PROMOTION>
140 struct ArithmeticPromotion; 148 struct MaxExponentPromotion;
141 149
142 template <typename Lhs, typename Rhs> 150 template <typename Lhs, typename Rhs>
143 struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> { 151 struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
144 typedef Lhs type; 152 using type = Lhs;
145 }; 153 };
146 154
147 template <typename Lhs, typename Rhs> 155 template <typename Lhs, typename Rhs>
148 struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> { 156 struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
149 typedef Rhs type; 157 using type = Rhs;
158 };
159
160 template <typename Lhs,
161 typename Rhs = Lhs,
162 bool is_intmax_type =
163 std::is_integral<
164 typename MaxExponentPromotion<Lhs, Rhs>::type>::value &&
165 sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) ==
166 sizeof(intmax_t),
167 bool is_max_exponent =
168 StaticDstRangeRelationToSrcRange<
169 typename MaxExponentPromotion<Lhs, Rhs>::type,
170 Lhs>::value ==
171 NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
172 typename MaxExponentPromotion<Lhs, Rhs>::type,
173 Rhs>::value == NUMERIC_RANGE_CONTAINED>
174 struct BigEnoughPromotion;
175
176 // The side with the max exponent is big enough.
177 template <typename Lhs, typename Rhs, bool is_intmax_type>
178 struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
179 using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
180 static const bool is_contained = true;
181 };
182
183 // We can use a twice wider type to fit.
184 template <typename Lhs, typename Rhs>
185 struct BigEnoughPromotion<Lhs, Rhs, false, false> {
186 using type = typename IntegerForSizeAndSign<
187 sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) * 2,
188 std::is_signed<Lhs>::value || std::is_signed<Rhs>::value>::type;
189 static const bool is_contained = true;
190 };
191
192 // No type is large enough.
193 template <typename Lhs, typename Rhs>
194 struct BigEnoughPromotion<Lhs, Rhs, true, false> {
195 using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
196 static const bool is_contained = false;
197 };
198
199 // These are the four supported promotion types.
200
201 // Use the type of the left-hand argument.
202 template <typename Lhs, typename Rhs>
203 struct ArithmeticPromotion<LEFT_PROMOTION, Lhs, Rhs> {
204 using type = Lhs;
205 static const bool is_contained = true;
206 };
207
208 // Use the type of the right-hand argument.
209 template <typename Lhs, typename Rhs>
210 struct ArithmeticPromotion<RIGHT_PROMOTION, Lhs, Rhs> {
211 using type = Rhs;
212 static const bool is_contained = true;
213 };
214
215 // Use the type supporting the largest exponent.
216 template <typename Lhs, typename Rhs>
217 struct ArithmeticPromotion<MAX_EXPONENT_PROMOTION, Lhs, Rhs> {
218 using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
219 static const bool is_contained = true;
220 };
221
222 // Attempt to find a big enough type.
223 template <typename Lhs, typename Rhs>
224 struct ArithmeticPromotion<BIG_ENOUGH_PROMOTION, Lhs, Rhs> {
225 using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
226 static const bool is_contained = BigEnoughPromotion<Lhs, Rhs>::is_contained;
150 }; 227 };
151 228
152 // Here are the actual portable checked integer math implementations. 229 // Here are the actual portable checked integer math implementations.
153 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean 230 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean
154 // way to coalesce things into the CheckedNumericState specializations below. 231 // way to coalesce things into the CheckedNumericState specializations below.
155 232
156 template <typename T> 233 template <typename T>
157 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type 234 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type
158 CheckedAddImpl(T x, T y, T* result) { 235 CheckedAddImpl(T x, T y, T* result) {
159 // Since the value of x+y is undefined if we have a signed type, we compute 236 // Since the value of x+y is undefined if we have a signed type, we compute
(...skipping 11 matching lines...) Expand all
171 : (BinaryComplement(x) >= 248 : (BinaryComplement(x) >=
172 y); // Unsigned is either valid or underflow. 249 y); // Unsigned is either valid or underflow.
173 } 250 }
174 251
175 template <typename T, typename U, typename V> 252 template <typename T, typename U, typename V>
176 typename std::enable_if<std::numeric_limits<T>::is_integer && 253 typename std::enable_if<std::numeric_limits<T>::is_integer &&
177 std::numeric_limits<U>::is_integer && 254 std::numeric_limits<U>::is_integer &&
178 std::numeric_limits<V>::is_integer, 255 std::numeric_limits<V>::is_integer,
179 bool>::type 256 bool>::type
180 CheckedAdd(T x, U y, V* result) { 257 CheckedAdd(T x, U y, V* result) {
181 using Promotion = typename ArithmeticPromotion<T, U>::type; 258 using Promotion =
259 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
182 // Fail if either operand is out of range for the promoted type. 260 // Fail if either operand is out of range for the promoted type.
183 // TODO(jschuh): This could be made to work for a broader range of values. 261 // TODO(jschuh): This could be made to work for a broader range of values.
184 if (!IsValueInRangeForNumericType<Promotion>(x) || 262 if (!IsValueInRangeForNumericType<Promotion>(x) ||
185 !IsValueInRangeForNumericType<Promotion>(y)) { 263 !IsValueInRangeForNumericType<Promotion>(y)) {
186 return false; 264 return false;
187 } 265 }
188 266
189 Promotion presult; 267 Promotion presult;
190 bool is_valid = CheckedAddImpl(static_cast<Promotion>(x), 268 bool is_valid = CheckedAddImpl(static_cast<Promotion>(x),
191 static_cast<Promotion>(y), &presult); 269 static_cast<Promotion>(y), &presult);
(...skipping 18 matching lines...) Expand all
210 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) 288 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))
211 : (x >= y); 289 : (x >= y);
212 } 290 }
213 291
214 template <typename T, typename U, typename V> 292 template <typename T, typename U, typename V>
215 typename std::enable_if<std::numeric_limits<T>::is_integer && 293 typename std::enable_if<std::numeric_limits<T>::is_integer &&
216 std::numeric_limits<U>::is_integer && 294 std::numeric_limits<U>::is_integer &&
217 std::numeric_limits<V>::is_integer, 295 std::numeric_limits<V>::is_integer,
218 bool>::type 296 bool>::type
219 CheckedSub(T x, U y, V* result) { 297 CheckedSub(T x, U y, V* result) {
220 using Promotion = typename ArithmeticPromotion<T, U>::type; 298 using Promotion =
299 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
221 // Fail if either operand is out of range for the promoted type. 300 // Fail if either operand is out of range for the promoted type.
222 // TODO(jschuh): This could be made to work for a broader range of values. 301 // TODO(jschuh): This could be made to work for a broader range of values.
223 if (!IsValueInRangeForNumericType<Promotion>(x) || 302 if (!IsValueInRangeForNumericType<Promotion>(x) ||
224 !IsValueInRangeForNumericType<Promotion>(y)) { 303 !IsValueInRangeForNumericType<Promotion>(y)) {
225 return false; 304 return false;
226 } 305 }
227 306
228 Promotion presult; 307 Promotion presult;
229 bool is_valid = CheckedSubImpl(static_cast<Promotion>(x), 308 bool is_valid = CheckedSubImpl(static_cast<Promotion>(x),
230 static_cast<Promotion>(y), &presult); 309 static_cast<Promotion>(y), &presult);
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 *result = x * y; 365 *result = x * y;
287 return (y == 0 || x <= std::numeric_limits<T>::max() / y); 366 return (y == 0 || x <= std::numeric_limits<T>::max() / y);
288 } 367 }
289 368
290 template <typename T, typename U, typename V> 369 template <typename T, typename U, typename V>
291 typename std::enable_if<std::numeric_limits<T>::is_integer && 370 typename std::enable_if<std::numeric_limits<T>::is_integer &&
292 std::numeric_limits<U>::is_integer && 371 std::numeric_limits<U>::is_integer &&
293 std::numeric_limits<V>::is_integer, 372 std::numeric_limits<V>::is_integer,
294 bool>::type 373 bool>::type
295 CheckedMul(T x, U y, V* result) { 374 CheckedMul(T x, U y, V* result) {
296 using Promotion = typename ArithmeticPromotion<T, U>::type; 375 using Promotion =
376 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
297 // Fail if either operand is out of range for the promoted type. 377 // Fail if either operand is out of range for the promoted type.
298 // TODO(jschuh): This could be made to work for a broader range of values. 378 // TODO(jschuh): This could be made to work for a broader range of values.
299 if (!IsValueInRangeForNumericType<Promotion>(x) || 379 if (!IsValueInRangeForNumericType<Promotion>(x) ||
300 !IsValueInRangeForNumericType<Promotion>(y)) { 380 !IsValueInRangeForNumericType<Promotion>(y)) {
301 return false; 381 return false;
302 } 382 }
303 383
304 Promotion presult; 384 Promotion presult;
305 bool is_valid = CheckedMulImpl(static_cast<Promotion>(x), 385 bool is_valid = CheckedMulImpl(static_cast<Promotion>(x),
306 static_cast<Promotion>(y), &presult); 386 static_cast<Promotion>(y), &presult);
(...skipping 13 matching lines...) Expand all
320 } 400 }
321 return false; 401 return false;
322 } 402 }
323 403
324 template <typename T, typename U, typename V> 404 template <typename T, typename U, typename V>
325 typename std::enable_if<std::numeric_limits<T>::is_integer && 405 typename std::enable_if<std::numeric_limits<T>::is_integer &&
326 std::numeric_limits<U>::is_integer && 406 std::numeric_limits<U>::is_integer &&
327 std::numeric_limits<V>::is_integer, 407 std::numeric_limits<V>::is_integer,
328 bool>::type 408 bool>::type
329 CheckedDiv(T x, U y, V* result) { 409 CheckedDiv(T x, U y, V* result) {
330 using Promotion = typename ArithmeticPromotion<T, U>::type; 410 using Promotion =
411 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
331 // Fail if either operand is out of range for the promoted type. 412 // Fail if either operand is out of range for the promoted type.
332 // TODO(jschuh): This could be made to work for a broader range of values. 413 // TODO(jschuh): This could be made to work for a broader range of values.
333 if (!IsValueInRangeForNumericType<Promotion>(x) || 414 if (!IsValueInRangeForNumericType<Promotion>(x) ||
334 !IsValueInRangeForNumericType<Promotion>(y)) { 415 !IsValueInRangeForNumericType<Promotion>(y)) {
335 return false; 416 return false;
336 } 417 }
337 418
338 Promotion presult; 419 Promotion presult;
339 bool is_valid = CheckedDivImpl(static_cast<Promotion>(x), 420 bool is_valid = CheckedDivImpl(static_cast<Promotion>(x),
340 static_cast<Promotion>(y), &presult); 421 static_cast<Promotion>(y), &presult);
(...skipping 24 matching lines...) Expand all
365 } 446 }
366 return false; 447 return false;
367 } 448 }
368 449
369 template <typename T, typename U, typename V> 450 template <typename T, typename U, typename V>
370 typename std::enable_if<std::numeric_limits<T>::is_integer && 451 typename std::enable_if<std::numeric_limits<T>::is_integer &&
371 std::numeric_limits<U>::is_integer && 452 std::numeric_limits<U>::is_integer &&
372 std::numeric_limits<V>::is_integer, 453 std::numeric_limits<V>::is_integer,
373 bool>::type 454 bool>::type
374 CheckedMod(T x, U y, V* result) { 455 CheckedMod(T x, U y, V* result) {
375 using Promotion = typename ArithmeticPromotion<T, U>::type; 456 using Promotion =
457 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type;
376 Promotion presult; 458 Promotion presult;
377 bool is_valid = CheckedModImpl(static_cast<Promotion>(x), 459 bool is_valid = CheckedModImpl(static_cast<Promotion>(x),
378 static_cast<Promotion>(y), &presult); 460 static_cast<Promotion>(y), &presult);
379 *result = static_cast<V>(presult); 461 *result = static_cast<V>(presult);
380 return is_valid && IsValueInRangeForNumericType<V>(presult); 462 return is_valid && IsValueInRangeForNumericType<V>(presult);
381 } 463 }
382 464
383 template <typename T> 465 template <typename T>
384 typename std::enable_if<std::numeric_limits<T>::is_integer && 466 typename std::enable_if<std::numeric_limits<T>::is_integer &&
385 std::numeric_limits<T>::is_signed, 467 std::numeric_limits<T>::is_signed,
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after
593 : value_(static_cast<T>(rhs.value())) {} 675 : value_(static_cast<T>(rhs.value())) {}
594 676
595 bool is_valid() const { return std::isfinite(value_); } 677 bool is_valid() const { return std::isfinite(value_); }
596 T value() const { return value_; } 678 T value() const { return value_; }
597 }; 679 };
598 680
599 } // namespace internal 681 } // namespace internal
600 } // namespace base 682 } // namespace base
601 683
602 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ 684 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_
OLDNEW
« base/numerics/safe_math.h ('K') | « base/numerics/safe_math.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698