OLD | NEW |
---|---|
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 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
122 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> | 122 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> |
123 PositionOfSignBit<T>::value); | 123 PositionOfSignBit<T>::value); |
124 } | 124 } |
125 | 125 |
126 // This wrapper undoes the standard integer promotions. | 126 // This wrapper undoes the standard integer promotions. |
127 template <typename T> | 127 template <typename T> |
128 constexpr T BinaryComplement(T x) { | 128 constexpr T BinaryComplement(T x) { |
129 return static_cast<T>(~x); | 129 return static_cast<T>(~x); |
130 } | 130 } |
131 | 131 |
132 // Return if a numeric value is negative regardless of type. | |
133 template <typename T, | |
134 typename std::enable_if<std::is_arithmetic<T>::value && | |
135 std::is_signed<T>::value>::type* = nullptr> | |
136 constexpr bool IsNegative(T x) { | |
137 return x < 0; | |
138 } | |
139 | |
140 template <typename T, | |
141 typename std::enable_if<std::is_arithmetic<T>::value && | |
142 !std::is_signed<T>::value>::type* = nullptr> | |
143 constexpr bool IsNegative(T x) { | |
144 return false; | |
145 } | |
146 | |
147 enum ArithmeticPromotionCategory { | 132 enum ArithmeticPromotionCategory { |
148 LEFT_PROMOTION, // Use the type of the left-hand argument. | 133 LEFT_PROMOTION, // Use the type of the left-hand argument. |
149 RIGHT_PROMOTION, // Use the type of the right-hand argument. | 134 RIGHT_PROMOTION, // Use the type of the right-hand argument. |
150 MAX_EXPONENT_PROMOTION, // Use the type supporting the largest exponent. | 135 MAX_EXPONENT_PROMOTION, // Use the type supporting the largest exponent. |
151 BIG_ENOUGH_PROMOTION // Attempt to find a big enough type. | 136 BIG_ENOUGH_PROMOTION // Attempt to find a big enough type. |
152 }; | 137 }; |
153 | 138 |
154 template <ArithmeticPromotionCategory Promotion, | 139 template <ArithmeticPromotionCategory Promotion, |
155 typename Lhs, | 140 typename Lhs, |
156 typename Rhs = Lhs> | 141 typename Rhs = Lhs> |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
206 static const bool is_contained = true; | 191 static const bool is_contained = true; |
207 }; | 192 }; |
208 | 193 |
209 // No type is large enough. | 194 // No type is large enough. |
210 template <typename Lhs, typename Rhs> | 195 template <typename Lhs, typename Rhs> |
211 struct BigEnoughPromotion<Lhs, Rhs, true, false> { | 196 struct BigEnoughPromotion<Lhs, Rhs, true, false> { |
212 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; | 197 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; |
213 static const bool is_contained = false; | 198 static const bool is_contained = false; |
214 }; | 199 }; |
215 | 200 |
216 // These are the four supported promotion types. | 201 // These are the supported promotion types. |
217 | |
218 // Use the type of the left-hand argument. | |
219 template <typename Lhs, typename Rhs> | |
220 struct ArithmeticPromotion<LEFT_PROMOTION, Lhs, Rhs> { | |
221 using type = Lhs; | |
222 static const bool is_contained = true; | |
223 }; | |
224 | |
225 // Use the type of the right-hand argument. | |
226 template <typename Lhs, typename Rhs> | |
227 struct ArithmeticPromotion<RIGHT_PROMOTION, Lhs, Rhs> { | |
228 using type = Rhs; | |
229 static const bool is_contained = true; | |
230 }; | |
231 | 202 |
232 // Use the type supporting the largest exponent. | 203 // Use the type supporting the largest exponent. |
233 template <typename Lhs, typename Rhs> | 204 template <typename Lhs, typename Rhs> |
234 struct ArithmeticPromotion<MAX_EXPONENT_PROMOTION, Lhs, Rhs> { | 205 struct ArithmeticPromotion<MAX_EXPONENT_PROMOTION, Lhs, Rhs> { |
235 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; | 206 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; |
236 static const bool is_contained = true; | 207 static const bool is_contained = true; |
237 }; | 208 }; |
238 | 209 |
239 // Attempt to find a big enough type. | 210 // Attempt to find a big enough type. |
240 template <typename Lhs, typename Rhs> | 211 template <typename Lhs, typename Rhs> |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
283 *result = static_cast<T>(uresult); | 254 *result = static_cast<T>(uresult); |
284 // Addition is valid if the sign of (x + y) is equal to either that of x or | 255 // Addition is valid if the sign of (x + y) is equal to either that of x or |
285 // that of y. | 256 // that of y. |
286 return (std::numeric_limits<T>::is_signed) | 257 return (std::numeric_limits<T>::is_signed) |
287 ? HasSignBit(BinaryComplement( | 258 ? HasSignBit(BinaryComplement( |
288 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) | 259 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) |
289 : (BinaryComplement(x) >= | 260 : (BinaryComplement(x) >= |
290 y); // Unsigned is either valid or underflow. | 261 y); // Unsigned is either valid or underflow. |
291 } | 262 } |
292 | 263 |
293 template <typename T, typename U, typename V> | 264 template <typename T, typename U, class Enable = void> |
294 typename std::enable_if<std::numeric_limits<T>::is_integer && | 265 struct CheckedAdd {}; |
295 std::numeric_limits<U>::is_integer && | 266 |
296 std::numeric_limits<V>::is_integer, | 267 template <typename T, typename U> |
297 bool>::type | 268 struct CheckedAdd< |
298 CheckedAdd(T x, U y, V* result) { | 269 T, |
270 U, | |
271 typename std::enable_if<std::numeric_limits<T>::is_integer && | |
272 std::numeric_limits<U>::is_integer>::type> { | |
273 using result_type = | |
274 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; | |
275 template <typename V> | |
276 static bool Op(T x, U y, V* result) { | |
299 #if USE_OVERFLOW_BUILTINS | 277 #if USE_OVERFLOW_BUILTINS |
300 return !__builtin_add_overflow(x, y, result); | 278 return !__builtin_add_overflow(x, y, result); |
301 #else | 279 #else |
302 using Promotion = | 280 using Promotion = |
303 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 281 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
304 Promotion presult; | 282 Promotion presult; |
305 // Fail if either operand is out of range for the promoted type. | 283 // Fail if either operand is out of range for the promoted type. |
306 // TODO(jschuh): This could be made to work for a broader range of values. | 284 // TODO(jschuh): This could be made to work for a broader range of values. |
307 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && | 285 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
308 IsValueInRangeForNumericType<Promotion>(y); | 286 IsValueInRangeForNumericType<Promotion>(y); |
309 | 287 |
310 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { | 288 if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { |
311 presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); | 289 presult = static_cast<Promotion>(x) + static_cast<Promotion>(y); |
312 } else { | 290 } else { |
313 is_valid &= CheckedAddImpl(static_cast<Promotion>(x), | 291 is_valid &= CheckedAddImpl(static_cast<Promotion>(x), |
314 static_cast<Promotion>(y), &presult); | 292 static_cast<Promotion>(y), &presult); |
293 } | |
294 *result = static_cast<V>(presult); | |
295 return is_valid && IsValueInRangeForNumericType<V>(presult); | |
296 #endif | |
315 } | 297 } |
316 *result = static_cast<V>(presult); | 298 }; |
317 return is_valid && IsValueInRangeForNumericType<V>(presult); | |
318 #endif | |
319 } | |
320 | 299 |
321 template <typename T> | 300 template <typename T> |
322 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 301 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
323 CheckedSubImpl(T x, T y, T* result) { | 302 CheckedSubImpl(T x, T y, T* result) { |
324 // Since the value of x+y is undefined if we have a signed type, we compute | 303 // Since the value of x+y is undefined if we have a signed type, we compute |
325 // it using the unsigned type of the same size. | 304 // it using the unsigned type of the same size. |
326 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; | 305 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; |
327 UnsignedDst ux = static_cast<UnsignedDst>(x); | 306 UnsignedDst ux = static_cast<UnsignedDst>(x); |
328 UnsignedDst uy = static_cast<UnsignedDst>(y); | 307 UnsignedDst uy = static_cast<UnsignedDst>(y); |
329 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); | 308 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); |
330 *result = static_cast<T>(uresult); | 309 *result = static_cast<T>(uresult); |
331 // Subtraction is valid if either x and y have same sign, or (x-y) and x have | 310 // Subtraction is valid if either x and y have same sign, or (x-y) and x have |
332 // the same sign. | 311 // the same sign. |
333 return (std::numeric_limits<T>::is_signed) | 312 return (std::numeric_limits<T>::is_signed) |
334 ? HasSignBit(BinaryComplement( | 313 ? HasSignBit(BinaryComplement( |
335 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) | 314 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) |
336 : (x >= y); | 315 : (x >= y); |
337 } | 316 } |
338 | 317 |
339 template <typename T, typename U, typename V> | 318 template <typename T, typename U, class Enable = void> |
340 typename std::enable_if<std::numeric_limits<T>::is_integer && | 319 struct CheckedSub {}; |
341 std::numeric_limits<U>::is_integer && | 320 |
342 std::numeric_limits<V>::is_integer, | 321 template <typename T, typename U> |
343 bool>::type | 322 struct CheckedSub< |
344 CheckedSub(T x, U y, V* result) { | 323 T, |
324 U, | |
325 typename std::enable_if<std::numeric_limits<T>::is_integer && | |
326 std::numeric_limits<U>::is_integer>::type> { | |
327 using result_type = | |
328 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; | |
329 template <typename V> | |
330 static bool Op(T x, U y, V* result) { | |
345 #if USE_OVERFLOW_BUILTINS | 331 #if USE_OVERFLOW_BUILTINS |
346 return !__builtin_sub_overflow(x, y, result); | 332 return !__builtin_sub_overflow(x, y, result); |
347 #else | 333 #else |
348 using Promotion = | 334 using Promotion = |
349 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 335 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
350 Promotion presult; | 336 Promotion presult; |
351 // Fail if either operand is out of range for the promoted type. | 337 // Fail if either operand is out of range for the promoted type. |
352 // TODO(jschuh): This could be made to work for a broader range of values. | 338 // TODO(jschuh): This could be made to work for a broader range of values. |
353 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && | 339 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
354 IsValueInRangeForNumericType<Promotion>(y); | 340 IsValueInRangeForNumericType<Promotion>(y); |
355 | 341 |
356 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { | 342 if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { |
357 presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); | 343 presult = static_cast<Promotion>(x) - static_cast<Promotion>(y); |
358 } else { | 344 } else { |
359 is_valid &= CheckedSubImpl(static_cast<Promotion>(x), | 345 is_valid &= CheckedSubImpl(static_cast<Promotion>(x), |
360 static_cast<Promotion>(y), &presult); | 346 static_cast<Promotion>(y), &presult); |
347 } | |
348 *result = static_cast<V>(presult); | |
349 return is_valid && IsValueInRangeForNumericType<V>(presult); | |
350 #endif | |
361 } | 351 } |
362 *result = static_cast<V>(presult); | 352 }; |
363 return is_valid && IsValueInRangeForNumericType<V>(presult); | |
364 #endif | |
365 } | |
366 | 353 |
367 // Integer multiplication is a bit complicated. In the fast case we just | 354 // Integer multiplication is a bit complicated. In the fast case we just |
368 // we just promote to a twice wider type, and range check the result. In the | 355 // we just promote to a twice wider type, and range check the result. In the |
369 // slow case we need to manually check that the result won't be truncated by | 356 // slow case we need to manually check that the result won't be truncated by |
370 // checking with division against the appropriate bound. | 357 // checking with division against the appropriate bound. |
371 template <typename T> | 358 template <typename T> |
372 typename std::enable_if<std::numeric_limits<T>::is_integer && | 359 typename std::enable_if<std::numeric_limits<T>::is_integer && |
373 sizeof(T) * 2 <= sizeof(uintmax_t), | 360 sizeof(T) * 2 <= sizeof(uintmax_t), |
374 bool>::type | 361 bool>::type |
375 CheckedMulImpl(T x, T y, T* result) { | 362 CheckedMulImpl(T x, T y, T* result) { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
412 template <typename T> | 399 template <typename T> |
413 typename std::enable_if<std::numeric_limits<T>::is_integer && | 400 typename std::enable_if<std::numeric_limits<T>::is_integer && |
414 !std::numeric_limits<T>::is_signed && | 401 !std::numeric_limits<T>::is_signed && |
415 (sizeof(T) * 2 > sizeof(uintmax_t)), | 402 (sizeof(T) * 2 > sizeof(uintmax_t)), |
416 bool>::type | 403 bool>::type |
417 CheckedMulImpl(T x, T y, T* result) { | 404 CheckedMulImpl(T x, T y, T* result) { |
418 *result = x * y; | 405 *result = x * y; |
419 return (y == 0 || x <= std::numeric_limits<T>::max() / y); | 406 return (y == 0 || x <= std::numeric_limits<T>::max() / y); |
420 } | 407 } |
421 | 408 |
422 template <typename T, typename U, typename V> | 409 template <typename T, typename U, class Enable = void> |
423 typename std::enable_if<std::numeric_limits<T>::is_integer && | 410 struct CheckedMul {}; |
424 std::numeric_limits<U>::is_integer && | 411 |
425 std::numeric_limits<V>::is_integer, | 412 template <typename T, typename U> |
426 bool>::type | 413 struct CheckedMul< |
427 CheckedMul(T x, U y, V* result) { | 414 T, |
415 U, | |
416 typename std::enable_if<std::numeric_limits<T>::is_integer && | |
417 std::numeric_limits<U>::is_integer>::type> { | |
418 using result_type = | |
419 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; | |
420 template <typename V> | |
421 static bool Op(T x, U y, V* result) { | |
428 #if USE_OVERFLOW_BUILTINS | 422 #if USE_OVERFLOW_BUILTINS |
429 #if defined(__clang__) | 423 #if defined(__clang__) |
430 // TODO(jschuh): Get the Clang runtime library issues sorted out so we can | 424 // TODO(jschuh): Get the Clang runtime library issues sorted out so we can |
431 // support full-width, mixed-sign multiply builtins. https://crbug.com/613003 | 425 // support full-width, mixed-sign multiply builtins. |
432 static const bool kUseMaxInt = | 426 // https://crbug.com/613003 |
433 sizeof(__typeof__(x * y)) < sizeof(intptr_t) || | 427 static const bool kUseMaxInt = |
434 (sizeof(__typeof__(x * y)) == sizeof(intptr_t) && | 428 sizeof(__typeof__(x * y)) < sizeof(intptr_t) || |
435 std::is_signed<T>::value == std::is_signed<U>::value); | 429 (sizeof(__typeof__(x * y)) == sizeof(intptr_t) && |
430 std::is_signed<T>::value == std::is_signed<U>::value); | |
436 #else | 431 #else |
437 static const bool kUseMaxInt = true; | 432 static const bool kUseMaxInt = true; |
438 #endif | 433 #endif |
439 if (kUseMaxInt) | 434 if (kUseMaxInt) |
440 return !__builtin_mul_overflow(x, y, result); | 435 return !__builtin_mul_overflow(x, y, result); |
441 #endif | 436 #endif |
442 using Promotion = | 437 using Promotion = |
443 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 438 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
444 Promotion presult; | 439 Promotion presult; |
445 // Fail if either operand is out of range for the promoted type. | 440 // Fail if either operand is out of range for the promoted type. |
446 // TODO(jschuh): This could be made to work for a broader range of values. | 441 // TODO(jschuh): This could be made to work for a broader range of values. |
447 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && | 442 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && |
448 IsValueInRangeForNumericType<Promotion>(y); | 443 IsValueInRangeForNumericType<Promotion>(y); |
449 | 444 |
450 if (IsIntegerArithmeticSafe<Promotion, U, V>::value) { | 445 if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { |
451 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); | 446 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); |
452 } else { | 447 } else { |
453 is_valid &= CheckedMulImpl(static_cast<Promotion>(x), | 448 is_valid &= CheckedMulImpl(static_cast<Promotion>(x), |
454 static_cast<Promotion>(y), &presult); | 449 static_cast<Promotion>(y), &presult); |
450 } | |
451 *result = static_cast<V>(presult); | |
452 return is_valid && IsValueInRangeForNumericType<V>(presult); | |
455 } | 453 } |
456 *result = static_cast<V>(presult); | 454 }; |
457 return is_valid && IsValueInRangeForNumericType<V>(presult); | |
458 } | |
459 | 455 |
460 // Avoid poluting the namespace once we're done with the macro. | 456 // Avoid poluting the namespace once we're done with the macro. |
461 #undef USE_OVERFLOW_BUILTINS | 457 #undef USE_OVERFLOW_BUILTINS |
462 | 458 |
463 // Division just requires a check for a zero denominator or an invalid negation | 459 // Division just requires a check for a zero denominator or an invalid negation |
464 // on signed min/-1. | 460 // on signed min/-1. |
465 template <typename T> | 461 template <typename T> |
466 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 462 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
467 CheckedDivImpl(T x, T y, T* result) { | 463 CheckedDivImpl(T x, T y, T* result) { |
468 if (y && (!std::numeric_limits<T>::is_signed || | 464 if (y && (!std::numeric_limits<T>::is_signed || |
469 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { | 465 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { |
470 *result = x / y; | 466 *result = x / y; |
471 return true; | 467 return true; |
472 } | 468 } |
473 return false; | 469 return false; |
474 } | 470 } |
475 | 471 |
476 template <typename T, typename U, typename V> | 472 template <typename T, typename U, class Enable = void> |
477 typename std::enable_if<std::numeric_limits<T>::is_integer && | 473 struct CheckedDiv {}; |
478 std::numeric_limits<U>::is_integer && | 474 |
479 std::numeric_limits<V>::is_integer, | 475 template <typename T, typename U> |
480 bool>::type | 476 struct CheckedDiv< |
481 CheckedDiv(T x, U y, V* result) { | 477 T, |
482 using Promotion = | 478 U, |
483 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 479 typename std::enable_if<std::numeric_limits<T>::is_integer && |
484 Promotion presult; | 480 std::numeric_limits<U>::is_integer>::type> { |
485 // Fail if either operand is out of range for the promoted type. | 481 using result_type = |
486 // TODO(jschuh): This could be made to work for a broader range of values. | 482 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; |
487 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && | 483 template <typename V> |
488 IsValueInRangeForNumericType<Promotion>(y); | 484 static bool Op(T x, U y, V* result) { |
489 is_valid &= CheckedDivImpl(static_cast<Promotion>(x), | 485 using Promotion = |
490 static_cast<Promotion>(y), &presult); | 486 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; |
491 *result = static_cast<V>(presult); | 487 Promotion presult; |
492 return is_valid && IsValueInRangeForNumericType<V>(presult); | 488 // Fail if either operand is out of range for the promoted type. |
493 } | 489 // TODO(jschuh): This could be made to work for a broader range of values. |
490 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && | |
491 IsValueInRangeForNumericType<Promotion>(y); | |
492 is_valid &= CheckedDivImpl(static_cast<Promotion>(x), | |
493 static_cast<Promotion>(y), &presult); | |
494 *result = static_cast<V>(presult); | |
495 return is_valid && IsValueInRangeForNumericType<V>(presult); | |
496 } | |
497 }; | |
494 | 498 |
495 template <typename T> | 499 template <typename T> |
496 typename std::enable_if<std::numeric_limits<T>::is_integer && | 500 typename std::enable_if<std::numeric_limits<T>::is_integer && |
497 std::numeric_limits<T>::is_signed, | 501 std::numeric_limits<T>::is_signed, |
498 bool>::type | 502 bool>::type |
499 CheckedModImpl(T x, T y, T* result) { | 503 CheckedModImpl(T x, T y, T* result) { |
500 if (y > 0) { | 504 if (y > 0) { |
501 *result = static_cast<T>(x % y); | 505 *result = static_cast<T>(x % y); |
502 return true; | 506 return true; |
503 } | 507 } |
504 return false; | 508 return false; |
505 } | 509 } |
506 | 510 |
507 template <typename T> | 511 template <typename T> |
508 typename std::enable_if<std::numeric_limits<T>::is_integer && | 512 typename std::enable_if<std::numeric_limits<T>::is_integer && |
509 !std::numeric_limits<T>::is_signed, | 513 !std::numeric_limits<T>::is_signed, |
510 bool>::type | 514 bool>::type |
511 CheckedModImpl(T x, T y, T* result) { | 515 CheckedModImpl(T x, T y, T* result) { |
512 if (y != 0) { | 516 if (y != 0) { |
513 *result = static_cast<T>(x % y); | 517 *result = static_cast<T>(x % y); |
514 return true; | 518 return true; |
515 } | 519 } |
516 return false; | 520 return false; |
517 } | 521 } |
518 | 522 |
519 template <typename T, typename U, typename V> | 523 template <typename T, typename U, class Enable = void> |
520 typename std::enable_if<std::numeric_limits<T>::is_integer && | 524 struct CheckedMod {}; |
521 std::numeric_limits<U>::is_integer && | 525 |
522 std::numeric_limits<V>::is_integer, | 526 template <typename T, typename U> |
523 bool>::type | 527 struct CheckedMod< |
524 CheckedMod(T x, U y, V* result) { | 528 T, |
525 using Promotion = | 529 U, |
526 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | 530 typename std::enable_if<std::numeric_limits<T>::is_integer && |
527 Promotion presult; | 531 std::numeric_limits<U>::is_integer>::type> { |
528 bool is_valid = CheckedModImpl(static_cast<Promotion>(x), | 532 using result_type = |
529 static_cast<Promotion>(y), &presult); | 533 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; |
530 *result = static_cast<V>(presult); | 534 template <typename V> |
531 return is_valid && IsValueInRangeForNumericType<V>(presult); | 535 static bool Op(T x, U y, V* result) { |
532 } | 536 using Promotion = |
537 typename ArithmeticPromotion<BIG_ENOUGH_PROMOTION, T, U>::type; | |
538 Promotion presult; | |
539 bool is_valid = CheckedModImpl(static_cast<Promotion>(x), | |
540 static_cast<Promotion>(y), &presult); | |
541 *result = static_cast<V>(presult); | |
542 return is_valid && IsValueInRangeForNumericType<V>(presult); | |
543 } | |
544 }; | |
545 | |
546 template <typename T, typename U, class Enable = void> | |
547 struct CheckedLeftShift {}; | |
533 | 548 |
534 // Left shift. Shifts less than 0 or greater than or equal to the number | 549 // Left shift. Shifts less than 0 or greater than or equal to the number |
535 // of bits in the promoted type are undefined. Shifts of negative values | 550 // of bits in the promoted type are undefined. Shifts of negative values |
536 // are undefined. Otherwise it is defined when the result fits. | 551 // are undefined. Otherwise it is defined when the result fits. |
537 template <typename T, typename U, typename V> | 552 template <typename T, typename U> |
538 typename std::enable_if<std::numeric_limits<T>::is_integer && | 553 struct CheckedLeftShift< |
539 std::numeric_limits<U>::is_integer && | 554 T, |
540 std::numeric_limits<V>::is_integer, | 555 U, |
541 bool>::type | 556 typename std::enable_if<std::numeric_limits<T>::is_integer && |
542 CheckedLeftShift(T x, U shift, V* result) { | 557 std::numeric_limits<U>::is_integer>::type> { |
543 using ShiftType = typename UnsignedIntegerForSize<T>::type; | 558 using result_type = T; |
544 static const ShiftType kBitWidth = CHAR_BIT * sizeof(T); | 559 template <typename V> |
545 const ShiftType real_shift = static_cast<ShiftType>(shift); | 560 static bool Op(T x, U shift, V* result) { |
546 // Signed shift is not legal on negative values. | 561 using ShiftType = typename UnsignedIntegerForSize<T>::type; |
547 if (!IsNegative(x) && real_shift < kBitWidth) { | 562 static const ShiftType kBitWidth = CHAR_BIT * sizeof(T); |
548 // Just use a multiplication because it's easy. | 563 const ShiftType real_shift = static_cast<ShiftType>(shift); |
549 // TODO(jschuh): This could probably be made more efficient. | 564 // Signed shift is not legal on negative values. |
550 if (!std::is_signed<T>::value || real_shift != kBitWidth - 1) | 565 if (!IsValueNegative(x) && real_shift < kBitWidth) { |
Tom Sepez
2016/11/21 17:19:52
nit: while we're at it, these might read easier if
jschuh
2016/11/21 17:43:25
I'm going to leave it for now, because when I look
| |
551 return CheckedMul(x, static_cast<T>(1) << shift, result); | 566 // Just use a multiplication because it's easy. |
552 return !x; // Special case zero for a full width signed shift. | 567 // TODO(jschuh): This could probably be made more efficient. |
568 if (!std::is_signed<T>::value || real_shift != kBitWidth - 1) | |
569 return CheckedMul<T, T>::Op(x, static_cast<T>(1) << shift, result); | |
570 return !x; // Special case zero for a full width signed shift. | |
571 } | |
572 return false; | |
553 } | 573 } |
554 return false; | 574 }; |
555 } | 575 |
576 template <typename T, typename U, class Enable = void> | |
577 struct CheckedRightShift {}; | |
556 | 578 |
557 // Right shift. Shifts less than 0 or greater than or equal to the number | 579 // Right shift. Shifts less than 0 or greater than or equal to the number |
558 // of bits in the promoted type are undefined. Otherwise, it is always defined, | 580 // of bits in the promoted type are undefined. Otherwise, it is always defined, |
559 // but a right shift of a negative value is implementation-dependent. | 581 // but a right shift of a negative value is implementation-dependent. |
560 template <typename T, typename U, typename V> | 582 template <typename T, typename U> |
561 typename std::enable_if<std::numeric_limits<T>::is_integer && | 583 struct CheckedRightShift< |
562 std::numeric_limits<U>::is_integer && | 584 T, |
563 std::numeric_limits<V>::is_integer, | 585 U, |
564 bool>::type | 586 typename std::enable_if<std::numeric_limits<T>::is_integer && |
565 CheckedRightShift(T x, U shift, V* result) { | 587 std::numeric_limits<U>::is_integer>::type> { |
566 // Use the type conversion push negative values out of range. | 588 using result_type = T; |
567 using ShiftType = typename UnsignedIntegerForSize<T>::type; | 589 template <typename V = result_type> |
568 if (static_cast<ShiftType>(shift) < (CHAR_BIT * sizeof(T))) { | 590 static bool Op(T x, U shift, V* result) { |
569 T tmp = x >> shift; | 591 // Use the type conversion push negative values out of range. |
570 *result = static_cast<V>(tmp); | 592 using ShiftType = typename UnsignedIntegerForSize<T>::type; |
571 return IsValueInRangeForNumericType<unsigned>(tmp); | 593 if (static_cast<ShiftType>(shift) < (CHAR_BIT * sizeof(T))) { |
594 T tmp = x >> shift; | |
595 *result = static_cast<V>(tmp); | |
596 return IsValueInRangeForNumericType<V>(tmp); | |
597 } | |
598 return false; | |
572 } | 599 } |
573 return false; | 600 }; |
574 } | |
575 | 601 |
576 template <typename T> | 602 template <typename T> |
577 typename std::enable_if<std::numeric_limits<T>::is_integer && | 603 typename std::enable_if<std::numeric_limits<T>::is_integer && |
578 std::numeric_limits<T>::is_signed, | 604 std::numeric_limits<T>::is_signed, |
579 bool>::type | 605 bool>::type |
580 CheckedNeg(T value, T* result) { | 606 CheckedNeg(T value, T* result) { |
581 // The negation of signed min is min, so catch that one. | 607 // The negation of signed min is min, so catch that one. |
582 if (value != std::numeric_limits<T>::min()) { | 608 if (value != std::numeric_limits<T>::min()) { |
583 *result = static_cast<T>(-value); | 609 *result = static_cast<T>(-value); |
584 return true; | 610 return true; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
633 | 659 |
634 template <typename T> | 660 template <typename T> |
635 typename std::enable_if<std::numeric_limits<T>::is_integer && | 661 typename std::enable_if<std::numeric_limits<T>::is_integer && |
636 !std::numeric_limits<T>::is_signed, | 662 !std::numeric_limits<T>::is_signed, |
637 T>::type | 663 T>::type |
638 SafeUnsignedAbs(T value) { | 664 SafeUnsignedAbs(T value) { |
639 // T is unsigned, so |value| must already be positive. | 665 // T is unsigned, so |value| must already be positive. |
640 return static_cast<T>(value); | 666 return static_cast<T>(value); |
641 } | 667 } |
642 | 668 |
643 template <typename T> | 669 // This is just boilerplate that wraps the standard floating point arithmetic. |
Tom Sepez
2016/11/21 17:19:52
Maybe this belongs in the safe_math.h file along w
jschuh
2016/11/21 17:43:25
Nah, this is the file with all of the integer impl
| |
644 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg( | 670 // A macro isn't the nicest solution, but it beats rewriting these repeatedly. |
645 T value, | 671 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ |
646 bool*) { | 672 template <typename T, typename U> \ |
647 NOTREACHED(); | 673 struct Checked##NAME<T, U, typename std::enable_if< \ |
648 return static_cast<T>(-value); | 674 std::numeric_limits<T>::is_iec559 || \ |
649 } | 675 std::numeric_limits<U>::is_iec559>::type> { \ |
676 using result_type = \ | |
677 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; \ | |
678 template <typename V> \ | |
679 static bool Op(T x, U y, V* result) { \ | |
680 using Promotion = \ | |
681 typename ArithmeticPromotion<MAX_EXPONENT_PROMOTION, T, U>::type; \ | |
682 Promotion presult = x OP y; \ | |
683 *result = static_cast<V>(presult); \ | |
684 return IsValueInRangeForNumericType<V>(presult); \ | |
685 } \ | |
686 }; | |
650 | 687 |
651 template <typename T> | 688 BASE_FLOAT_ARITHMETIC_OPS(Add, +) |
652 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( | 689 BASE_FLOAT_ARITHMETIC_OPS(Sub, -) |
653 T value, | 690 BASE_FLOAT_ARITHMETIC_OPS(Mul, *) |
654 bool*) { | 691 BASE_FLOAT_ARITHMETIC_OPS(Div, /) |
655 NOTREACHED(); | |
656 return static_cast<T>(std::abs(value)); | |
657 } | |
658 | 692 |
659 // These are the floating point stubs that the compiler needs to see. | 693 #undef BASE_FLOAT_ARITHMETIC_OPS |
660 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \ | |
661 template <typename T, typename U, typename V> \ | |
662 typename std::enable_if<std::numeric_limits<T>::is_iec559 || \ | |
663 std::numeric_limits<U>::is_iec559 || \ | |
664 std::numeric_limits<V>::is_iec559, \ | |
665 bool>::type Checked##NAME(T, U, V*) { \ | |
666 NOTREACHED(); \ | |
667 return static_cast<T>(false); \ | |
668 } | |
669 | |
670 BASE_FLOAT_ARITHMETIC_STUBS(Add) | |
671 BASE_FLOAT_ARITHMETIC_STUBS(Sub) | |
672 BASE_FLOAT_ARITHMETIC_STUBS(Mul) | |
673 BASE_FLOAT_ARITHMETIC_STUBS(Div) | |
674 BASE_FLOAT_ARITHMETIC_STUBS(Mod) | |
675 | |
676 #undef BASE_FLOAT_ARITHMETIC_STUBS | |
677 | 694 |
678 template <typename T> | 695 template <typename T> |
679 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type | 696 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type |
680 CheckedNeg(T value, T* result) { | 697 CheckedNeg(T value, T* result) { |
681 *result = static_cast<T>(-value); | 698 *result = static_cast<T>(-value); |
682 return true; | 699 return true; |
683 } | 700 } |
684 | 701 |
685 template <typename T> | 702 template <typename T> |
686 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type | 703 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
786 : value_(static_cast<T>(rhs.value())) {} | 803 : value_(static_cast<T>(rhs.value())) {} |
787 | 804 |
788 bool is_valid() const { return std::isfinite(value_); } | 805 bool is_valid() const { return std::isfinite(value_); } |
789 T value() const { return value_; } | 806 T value() const { return value_; } |
790 }; | 807 }; |
791 | 808 |
792 } // namespace internal | 809 } // namespace internal |
793 } // namespace base | 810 } // namespace base |
794 | 811 |
795 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 812 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
OLD | NEW |