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 |