Chromium Code Reviews| 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 |