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 |
| 131 // with the larger maximum exponent take precedence. |
| 132 enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION }; |
| 133 |
| 134 template <typename Lhs, |
| 135 typename Rhs = Lhs, |
| 136 ArithmeticPromotionCategory Promotion = |
| 137 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) |
| 138 ? LEFT_PROMOTION |
| 139 : RIGHT_PROMOTION> |
| 140 struct ArithmeticPromotion; |
| 141 |
| 142 template <typename Lhs, typename Rhs> |
| 143 struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> { |
| 144 typedef Lhs type; |
| 145 }; |
| 146 |
| 147 template <typename Lhs, typename Rhs> |
| 148 struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> { |
| 149 typedef Rhs type; |
| 150 }; |
| 151 |
130 // Here are the actual portable checked integer math implementations. | 152 // Here are the actual portable checked integer math implementations. |
131 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean | 153 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean |
132 // way to coalesce things into the CheckedNumericState specializations below. | 154 // way to coalesce things into the CheckedNumericState specializations below. |
133 | 155 |
134 template <typename T> | 156 template <typename T> |
135 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 157 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
136 CheckedAdd(T x, T y, T* result) { | 158 CheckedAddImpl(T x, T y, T* result) { |
137 // Since the value of x+y is undefined if we have a signed type, we compute | 159 // Since the value of x+y is undefined if we have a signed type, we compute |
138 // it using the unsigned type of the same size. | 160 // it using the unsigned type of the same size. |
139 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; | 161 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; |
140 UnsignedDst ux = static_cast<UnsignedDst>(x); | 162 UnsignedDst ux = static_cast<UnsignedDst>(x); |
141 UnsignedDst uy = static_cast<UnsignedDst>(y); | 163 UnsignedDst uy = static_cast<UnsignedDst>(y); |
142 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); | 164 UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy); |
143 *result = static_cast<T>(uresult); | 165 *result = static_cast<T>(uresult); |
144 // Addition is valid if the sign of (x + y) is equal to either that of x or | 166 // Addition is valid if the sign of (x + y) is equal to either that of x or |
145 // that of y. | 167 // that of y. |
146 return (std::numeric_limits<T>::is_signed) | 168 return (std::numeric_limits<T>::is_signed) |
147 ? HasSignBit(BinaryComplement( | 169 ? HasSignBit(BinaryComplement( |
148 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) | 170 static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy)))) |
149 : (BinaryComplement(x) >= | 171 : (BinaryComplement(x) >= |
150 y); // Unsigned is either valid or underflow. | 172 y); // Unsigned is either valid or underflow. |
151 } | 173 } |
152 | 174 |
| 175 template <typename T, typename U, typename V> |
| 176 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| 177 std::numeric_limits<U>::is_integer && |
| 178 std::numeric_limits<V>::is_integer, |
| 179 bool>::type |
| 180 CheckedAdd(T x, U y, V* result) { |
| 181 using Promotion = typename ArithmeticPromotion<T, U>::type; |
| 182 // 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. |
| 184 if (!IsValueInRangeForNumericType<Promotion>(x) || |
| 185 !IsValueInRangeForNumericType<Promotion>(y)) { |
| 186 return false; |
| 187 } |
| 188 |
| 189 Promotion presult; |
| 190 bool is_valid = CheckedAddImpl(static_cast<Promotion>(x), |
| 191 static_cast<Promotion>(y), &presult); |
| 192 *result = static_cast<V>(presult); |
| 193 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 194 } |
| 195 |
153 template <typename T> | 196 template <typename T> |
154 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 197 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
155 CheckedSub(T x, T y, T* result) { | 198 CheckedSubImpl(T x, T y, T* result) { |
156 // Since the value of x+y is undefined if we have a signed type, we compute | 199 // Since the value of x+y is undefined if we have a signed type, we compute |
157 // it using the unsigned type of the same size. | 200 // it using the unsigned type of the same size. |
158 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; | 201 typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; |
159 UnsignedDst ux = static_cast<UnsignedDst>(x); | 202 UnsignedDst ux = static_cast<UnsignedDst>(x); |
160 UnsignedDst uy = static_cast<UnsignedDst>(y); | 203 UnsignedDst uy = static_cast<UnsignedDst>(y); |
161 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); | 204 UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy); |
162 *result = static_cast<T>(uresult); | 205 *result = static_cast<T>(uresult); |
163 // Subtraction is valid if either x and y have same sign, or (x-y) and x have | 206 // Subtraction is valid if either x and y have same sign, or (x-y) and x have |
164 // the same sign. | 207 // the same sign. |
165 return (std::numeric_limits<T>::is_signed) | 208 return (std::numeric_limits<T>::is_signed) |
166 ? HasSignBit(BinaryComplement( | 209 ? HasSignBit(BinaryComplement( |
167 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) | 210 static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy)))) |
168 : (x >= y); | 211 : (x >= y); |
169 } | 212 } |
170 | 213 |
| 214 template <typename T, typename U, typename V> |
| 215 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| 216 std::numeric_limits<U>::is_integer && |
| 217 std::numeric_limits<V>::is_integer, |
| 218 bool>::type |
| 219 CheckedSub(T x, U y, V* result) { |
| 220 using Promotion = typename ArithmeticPromotion<T, U>::type; |
| 221 // 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. |
| 223 if (!IsValueInRangeForNumericType<Promotion>(x) || |
| 224 !IsValueInRangeForNumericType<Promotion>(y)) { |
| 225 return false; |
| 226 } |
| 227 |
| 228 Promotion presult; |
| 229 bool is_valid = CheckedSubImpl(static_cast<Promotion>(x), |
| 230 static_cast<Promotion>(y), &presult); |
| 231 *result = static_cast<V>(presult); |
| 232 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 233 } |
| 234 |
171 // Integer multiplication is a bit complicated. In the fast case we just | 235 // Integer multiplication is a bit complicated. In the fast case we just |
172 // we just promote to a twice wider type, and range check the result. In the | 236 // we just promote to a twice wider type, and range check the result. In the |
173 // slow case we need to manually check that the result won't be truncated by | 237 // slow case we need to manually check that the result won't be truncated by |
174 // checking with division against the appropriate bound. | 238 // checking with division against the appropriate bound. |
175 template <typename T> | 239 template <typename T> |
176 typename std::enable_if<std::numeric_limits<T>::is_integer && | 240 typename std::enable_if<std::numeric_limits<T>::is_integer && |
177 sizeof(T) * 2 <= sizeof(uintmax_t), | 241 sizeof(T) * 2 <= sizeof(uintmax_t), |
178 bool>::type | 242 bool>::type |
179 CheckedMul(T x, T y, T* result) { | 243 CheckedMulImpl(T x, T y, T* result) { |
180 typedef typename TwiceWiderInteger<T>::type IntermediateType; | 244 typedef typename TwiceWiderInteger<T>::type IntermediateType; |
181 IntermediateType tmp = | 245 IntermediateType tmp = |
182 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y); | 246 static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y); |
183 *result = static_cast<T>(tmp); | 247 *result = static_cast<T>(tmp); |
184 return DstRangeRelationToSrcRange<T>(tmp) == RANGE_VALID; | 248 return DstRangeRelationToSrcRange<T>(tmp) == RANGE_VALID; |
185 } | 249 } |
186 | 250 |
187 template <typename T> | 251 template <typename T> |
188 typename std::enable_if<std::numeric_limits<T>::is_integer && | 252 typename std::enable_if<std::numeric_limits<T>::is_integer && |
189 std::numeric_limits<T>::is_signed && | 253 std::numeric_limits<T>::is_signed && |
190 (sizeof(T) * 2 > sizeof(uintmax_t)), | 254 (sizeof(T) * 2 > sizeof(uintmax_t)), |
191 bool>::type | 255 bool>::type |
192 CheckedMul(T x, T y, T* result) { | 256 CheckedMulImpl(T x, T y, T* result) { |
193 if (x && y) { | 257 if (x && y) { |
194 if (x > 0) { | 258 if (x > 0) { |
195 if (y > 0) { | 259 if (y > 0) { |
196 if (x > std::numeric_limits<T>::max() / y) | 260 if (x > std::numeric_limits<T>::max() / y) |
197 return false; | 261 return false; |
198 } else { | 262 } else { |
199 if (y < std::numeric_limits<T>::min() / x) | 263 if (y < std::numeric_limits<T>::min() / x) |
200 return false; | 264 return false; |
201 } | 265 } |
202 } else { | 266 } else { |
203 if (y > 0) { | 267 if (y > 0) { |
204 if (x < std::numeric_limits<T>::min() / y) | 268 if (x < std::numeric_limits<T>::min() / y) |
205 return false; | 269 return false; |
206 } else { | 270 } else { |
207 if (y < std::numeric_limits<T>::max() / x) | 271 if (y < std::numeric_limits<T>::max() / x) |
208 return false; | 272 return false; |
209 } | 273 } |
210 } | 274 } |
211 } | 275 } |
212 *result = x * y; | 276 *result = x * y; |
213 return true; | 277 return true; |
214 } | 278 } |
215 | 279 |
216 template <typename T> | 280 template <typename T> |
217 typename std::enable_if<std::numeric_limits<T>::is_integer && | 281 typename std::enable_if<std::numeric_limits<T>::is_integer && |
218 !std::numeric_limits<T>::is_signed && | 282 !std::numeric_limits<T>::is_signed && |
219 (sizeof(T) * 2 > sizeof(uintmax_t)), | 283 (sizeof(T) * 2 > sizeof(uintmax_t)), |
220 bool>::type | 284 bool>::type |
221 CheckedMul(T x, T y, T* result) { | 285 CheckedMulImpl(T x, T y, T* result) { |
222 *result = x * y; | 286 *result = x * y; |
223 return (y == 0 || x <= std::numeric_limits<T>::max() / y); | 287 return (y == 0 || x <= std::numeric_limits<T>::max() / y); |
224 } | 288 } |
225 | 289 |
| 290 template <typename T, typename U, typename V> |
| 291 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| 292 std::numeric_limits<U>::is_integer && |
| 293 std::numeric_limits<V>::is_integer, |
| 294 bool>::type |
| 295 CheckedMul(T x, U y, V* result) { |
| 296 using Promotion = typename ArithmeticPromotion<T, U>::type; |
| 297 // 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. |
| 299 if (!IsValueInRangeForNumericType<Promotion>(x) || |
| 300 !IsValueInRangeForNumericType<Promotion>(y)) { |
| 301 return false; |
| 302 } |
| 303 |
| 304 Promotion presult; |
| 305 bool is_valid = CheckedMulImpl(static_cast<Promotion>(x), |
| 306 static_cast<Promotion>(y), &presult); |
| 307 *result = static_cast<V>(presult); |
| 308 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 309 } |
| 310 |
226 // Division just requires a check for a zero denominator or an invalid negation | 311 // Division just requires a check for a zero denominator or an invalid negation |
227 // on signed min/-1. | 312 // on signed min/-1. |
228 template <typename T> | 313 template <typename T> |
229 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type | 314 typename std::enable_if<std::numeric_limits<T>::is_integer, bool>::type |
230 CheckedDiv(T x, T y, T* result) { | 315 CheckedDivImpl(T x, T y, T* result) { |
231 if (y && (!std::numeric_limits<T>::is_signed || | 316 if (y && (!std::numeric_limits<T>::is_signed || |
232 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { | 317 x != std::numeric_limits<T>::min() || y != static_cast<T>(-1))) { |
233 *result = x / y; | 318 *result = x / y; |
234 return true; | 319 return true; |
235 } | 320 } |
236 return false; | 321 return false; |
237 } | 322 } |
238 | 323 |
| 324 template <typename T, typename U, typename V> |
| 325 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| 326 std::numeric_limits<U>::is_integer && |
| 327 std::numeric_limits<V>::is_integer, |
| 328 bool>::type |
| 329 CheckedDiv(T x, U y, V* result) { |
| 330 using Promotion = typename ArithmeticPromotion<T, U>::type; |
| 331 // 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. |
| 333 if (!IsValueInRangeForNumericType<Promotion>(x) || |
| 334 !IsValueInRangeForNumericType<Promotion>(y)) { |
| 335 return false; |
| 336 } |
| 337 |
| 338 Promotion presult; |
| 339 bool is_valid = CheckedDivImpl(static_cast<Promotion>(x), |
| 340 static_cast<Promotion>(y), &presult); |
| 341 *result = static_cast<V>(presult); |
| 342 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 343 } |
| 344 |
239 template <typename T> | 345 template <typename T> |
240 typename std::enable_if<std::numeric_limits<T>::is_integer && | 346 typename std::enable_if<std::numeric_limits<T>::is_integer && |
241 std::numeric_limits<T>::is_signed, | 347 std::numeric_limits<T>::is_signed, |
242 bool>::type | 348 bool>::type |
243 CheckedMod(T x, T y, T* result) { | 349 CheckedModImpl(T x, T y, T* result) { |
244 if (y > 0) { | 350 if (y > 0) { |
245 *result = static_cast<T>(x % y); | 351 *result = static_cast<T>(x % y); |
246 return true; | 352 return true; |
247 } | 353 } |
248 return false; | 354 return false; |
249 } | 355 } |
250 | 356 |
251 template <typename T> | 357 template <typename T> |
252 typename std::enable_if<std::numeric_limits<T>::is_integer && | 358 typename std::enable_if<std::numeric_limits<T>::is_integer && |
253 !std::numeric_limits<T>::is_signed, | 359 !std::numeric_limits<T>::is_signed, |
254 bool>::type | 360 bool>::type |
255 CheckedMod(T x, T y, T* result) { | 361 CheckedModImpl(T x, T y, T* result) { |
256 if (y != 0) { | 362 if (y != 0) { |
257 *result = static_cast<T>(x % y); | 363 *result = static_cast<T>(x % y); |
258 return true; | 364 return true; |
259 } | 365 } |
260 return false; | 366 return false; |
261 } | 367 } |
262 | 368 |
| 369 template <typename T, typename U, typename V> |
| 370 typename std::enable_if<std::numeric_limits<T>::is_integer && |
| 371 std::numeric_limits<U>::is_integer && |
| 372 std::numeric_limits<V>::is_integer, |
| 373 bool>::type |
| 374 CheckedMod(T x, U y, V* result) { |
| 375 using Promotion = typename ArithmeticPromotion<T, U>::type; |
| 376 Promotion presult; |
| 377 bool is_valid = CheckedModImpl(static_cast<Promotion>(x), |
| 378 static_cast<Promotion>(y), &presult); |
| 379 *result = static_cast<V>(presult); |
| 380 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 381 } |
| 382 |
263 template <typename T> | 383 template <typename T> |
264 typename std::enable_if<std::numeric_limits<T>::is_integer && | 384 typename std::enable_if<std::numeric_limits<T>::is_integer && |
265 std::numeric_limits<T>::is_signed, | 385 std::numeric_limits<T>::is_signed, |
266 bool>::type | 386 bool>::type |
267 CheckedNeg(T value, T* result) { | 387 CheckedNeg(T value, T* result) { |
268 // The negation of signed min is min, so catch that one. | 388 // The negation of signed min is min, so catch that one. |
269 if (value != std::numeric_limits<T>::min()) { | 389 if (value != std::numeric_limits<T>::min()) { |
270 *result = static_cast<T>(-value); | 390 *result = static_cast<T>(-value); |
271 return true; | 391 return true; |
272 } | 392 } |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 | 457 |
338 template <typename T> | 458 template <typename T> |
339 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( | 459 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( |
340 T value, | 460 T value, |
341 bool*) { | 461 bool*) { |
342 NOTREACHED(); | 462 NOTREACHED(); |
343 return static_cast<T>(std::abs(value)); | 463 return static_cast<T>(std::abs(value)); |
344 } | 464 } |
345 | 465 |
346 // These are the floating point stubs that the compiler needs to see. | 466 // These are the floating point stubs that the compiler needs to see. |
347 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \ | 467 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \ |
348 template <typename T> \ | 468 template <typename T, typename U, typename V> \ |
349 typename std::enable_if<std::numeric_limits<T>::is_iec559, bool>::type \ | 469 typename std::enable_if<std::numeric_limits<T>::is_iec559 || \ |
350 Checked##NAME(T, T, T*) { \ | 470 std::numeric_limits<U>::is_iec559 || \ |
351 NOTREACHED(); \ | 471 std::numeric_limits<V>::is_iec559, \ |
352 return static_cast<T>(false); \ | 472 bool>::type Checked##NAME(T, U, V*) { \ |
| 473 NOTREACHED(); \ |
| 474 return static_cast<T>(false); \ |
353 } | 475 } |
354 | 476 |
355 BASE_FLOAT_ARITHMETIC_STUBS(Add) | 477 BASE_FLOAT_ARITHMETIC_STUBS(Add) |
356 BASE_FLOAT_ARITHMETIC_STUBS(Sub) | 478 BASE_FLOAT_ARITHMETIC_STUBS(Sub) |
357 BASE_FLOAT_ARITHMETIC_STUBS(Mul) | 479 BASE_FLOAT_ARITHMETIC_STUBS(Mul) |
358 BASE_FLOAT_ARITHMETIC_STUBS(Div) | 480 BASE_FLOAT_ARITHMETIC_STUBS(Div) |
359 BASE_FLOAT_ARITHMETIC_STUBS(Mod) | 481 BASE_FLOAT_ARITHMETIC_STUBS(Mod) |
360 | 482 |
361 #undef BASE_FLOAT_ARITHMETIC_STUBS | 483 #undef BASE_FLOAT_ARITHMETIC_STUBS |
362 | 484 |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
467 | 589 |
468 // Copy constructor. | 590 // Copy constructor. |
469 template <typename Src> | 591 template <typename Src> |
470 CheckedNumericState(const CheckedNumericState<Src>& rhs) | 592 CheckedNumericState(const CheckedNumericState<Src>& rhs) |
471 : value_(static_cast<T>(rhs.value())) {} | 593 : value_(static_cast<T>(rhs.value())) {} |
472 | 594 |
473 bool is_valid() const { return std::isfinite(value_); } | 595 bool is_valid() const { return std::isfinite(value_); } |
474 T value() const { return value_; } | 596 T value() const { return value_; } |
475 }; | 597 }; |
476 | 598 |
477 // For integers less than 128-bit and floats 32-bit or larger, we have the type | |
478 // with the larger maximum exponent take precedence. | |
479 enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION }; | |
480 | |
481 template <typename Lhs, | |
482 typename Rhs = Lhs, | |
483 ArithmeticPromotionCategory Promotion = | |
484 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) | |
485 ? LEFT_PROMOTION | |
486 : RIGHT_PROMOTION> | |
487 struct ArithmeticPromotion; | |
488 | |
489 template <typename Lhs, typename Rhs> | |
490 struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> { | |
491 typedef Lhs type; | |
492 }; | |
493 | |
494 template <typename Lhs, typename Rhs> | |
495 struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> { | |
496 typedef Rhs type; | |
497 }; | |
498 | |
499 // We can statically check if operations on the provided types can wrap, so we | |
500 // can skip the checked operations if they're not needed. So, for an integer we | |
501 // care if the destination type preserves the sign and is twice the width of | |
502 // the source. | |
503 template <typename T, typename Lhs, typename Rhs> | |
504 struct IsIntegerArithmeticSafe { | |
505 static const bool value = !std::numeric_limits<T>::is_iec559 && | |
506 StaticDstRangeRelationToSrcRange<T, Lhs>::value == | |
507 NUMERIC_RANGE_CONTAINED && | |
508 sizeof(T) >= (2 * sizeof(Lhs)) && | |
509 StaticDstRangeRelationToSrcRange<T, Rhs>::value != | |
510 NUMERIC_RANGE_CONTAINED && | |
511 sizeof(T) >= (2 * sizeof(Rhs)); | |
512 }; | |
513 | |
514 } // namespace internal | 599 } // namespace internal |
515 } // namespace base | 600 } // namespace base |
516 | 601 |
517 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 602 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
OLD | NEW |