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 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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_ |
| OLD | NEW |