| 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_CONVERSIONS_IMPL_H_ | 5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
| 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
| 7 | 7 |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <limits> | 10 #include <limits> |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 129 | 129 |
| 130 // Signed to unsigned: Dst cannot be statically determined to contain Src. | 130 // Signed to unsigned: Dst cannot be statically determined to contain Src. |
| 131 template <typename Dst, typename Src> | 131 template <typename Dst, typename Src> |
| 132 struct StaticDstRangeRelationToSrcRange<Dst, | 132 struct StaticDstRangeRelationToSrcRange<Dst, |
| 133 Src, | 133 Src, |
| 134 INTEGER_REPRESENTATION_UNSIGNED, | 134 INTEGER_REPRESENTATION_UNSIGNED, |
| 135 INTEGER_REPRESENTATION_SIGNED> { | 135 INTEGER_REPRESENTATION_SIGNED> { |
| 136 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; | 136 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; |
| 137 }; | 137 }; |
| 138 | 138 |
| 139 enum RangeConstraint { | |
| 140 RANGE_VALID = 0x0, // Value can be represented by the destination type. | |
| 141 RANGE_UNDERFLOW = 0x1, // Value would underflow. | |
| 142 RANGE_OVERFLOW = 0x2, // Value would overflow. | |
| 143 RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). | |
| 144 }; | |
| 145 | |
| 146 // This class wraps the range constraints as separate booleans so the compiler | 139 // This class wraps the range constraints as separate booleans so the compiler |
| 147 // can identify constants and eliminate unused code paths. | 140 // can identify constants and eliminate unused code paths. |
| 148 class RangeCheck { | 141 class RangeCheck { |
| 149 public: | 142 public: |
| 150 constexpr RangeCheck(bool is_in_upper_bound, bool is_in_lower_bound) | 143 constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound) |
| 151 : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {} | 144 : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {} |
| 152 constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {} | 145 constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {} |
| 153 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } | 146 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } |
| 154 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } | 147 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } |
| 155 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } | 148 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } |
| 156 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } | 149 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } |
| 157 constexpr bool IsOverflowFlagSet() const { return is_overflow_; } | 150 constexpr bool IsOverflowFlagSet() const { return is_overflow_; } |
| 158 constexpr bool IsUnderflowFlagSet() const { return is_underflow_; } | 151 constexpr bool IsUnderflowFlagSet() const { return is_underflow_; } |
| 159 | 152 constexpr bool operator==(const RangeCheck rhs) const { |
| 160 // These are some wrappers to make the tests a bit cleaner. | 153 return is_underflow_ == rhs.is_underflow_ && |
| 161 constexpr operator RangeConstraint() const { | 154 is_overflow_ == rhs.is_overflow_; |
| 162 return static_cast<RangeConstraint>(static_cast<int>(is_overflow_) << 1 | | |
| 163 static_cast<int>(is_underflow_)); | |
| 164 } | 155 } |
| 165 constexpr bool operator==(const RangeConstraint rhs) const { | 156 constexpr bool operator!=(const RangeCheck rhs) const { |
| 166 return rhs == static_cast<RangeConstraint>(*this); | 157 return !(*this == rhs); |
| 167 } | 158 } |
| 168 | 159 |
| 169 private: | 160 private: |
| 170 // Do not change the order of these member variables. The integral conversion | 161 // Do not change the order of these member variables. The integral conversion |
| 171 // optimization depends on this exact order. | 162 // optimization depends on this exact order. |
| 172 const bool is_underflow_ : 1; | 163 const bool is_underflow_; |
| 173 const bool is_overflow_ : 1; | 164 const bool is_overflow_; |
| 174 }; | 165 }; |
| 175 | 166 |
| 176 // The following helper template addresses a corner case in range checks for | 167 // The following helper template addresses a corner case in range checks for |
| 177 // conversion from a floating-point type to an integral type of smaller range | 168 // conversion from a floating-point type to an integral type of smaller range |
| 178 // but larger precision (e.g. float -> unsigned). The problem is as follows: | 169 // but larger precision (e.g. float -> unsigned). The problem is as follows: |
| 179 // 1. Integral maximum is always one less than a power of two, so it must be | 170 // 1. Integral maximum is always one less than a power of two, so it must be |
| 180 // truncated to fit the mantissa of the floating point. The direction of | 171 // truncated to fit the mantissa of the floating point. The direction of |
| 181 // rounding is implementation defined, but by default it's always IEEE | 172 // rounding is implementation defined, but by default it's always IEEE |
| 182 // floats, which round to nearest and thus result in a value of larger | 173 // floats, which round to nearest and thus result in a value of larger |
| 183 // magnitude than the integral value. | 174 // magnitude than the integral value. |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 258 struct DstRangeRelationToSrcRangeImpl<Dst, | 249 struct DstRangeRelationToSrcRangeImpl<Dst, |
| 259 Src, | 250 Src, |
| 260 Bounds, | 251 Bounds, |
| 261 DstSign, | 252 DstSign, |
| 262 SrcSign, | 253 SrcSign, |
| 263 NUMERIC_RANGE_CONTAINED> { | 254 NUMERIC_RANGE_CONTAINED> { |
| 264 static constexpr RangeCheck Check(Src value) { | 255 static constexpr RangeCheck Check(Src value) { |
| 265 using SrcLimits = std::numeric_limits<Src>; | 256 using SrcLimits = std::numeric_limits<Src>; |
| 266 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 257 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 267 return RangeCheck( | 258 return RangeCheck( |
| 259 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || |
| 260 static_cast<Dst>(value) >= DstLimits::lowest(), |
| 268 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || | 261 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || |
| 269 static_cast<Dst>(value) <= DstLimits::max(), | 262 static_cast<Dst>(value) <= DstLimits::max()); |
| 270 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || | |
| 271 static_cast<Dst>(value) >= DstLimits::lowest()); | |
| 272 } | 263 } |
| 273 }; | 264 }; |
| 274 | 265 |
| 275 // Signed to signed narrowing: Both the upper and lower boundaries may be | 266 // Signed to signed narrowing: Both the upper and lower boundaries may be |
| 276 // exceeded for standard limits. | 267 // exceeded for standard limits. |
| 277 template <typename Dst, typename Src, template <typename> class Bounds> | 268 template <typename Dst, typename Src, template <typename> class Bounds> |
| 278 struct DstRangeRelationToSrcRangeImpl<Dst, | 269 struct DstRangeRelationToSrcRangeImpl<Dst, |
| 279 Src, | 270 Src, |
| 280 Bounds, | 271 Bounds, |
| 281 INTEGER_REPRESENTATION_SIGNED, | 272 INTEGER_REPRESENTATION_SIGNED, |
| 282 INTEGER_REPRESENTATION_SIGNED, | 273 INTEGER_REPRESENTATION_SIGNED, |
| 283 NUMERIC_RANGE_NOT_CONTAINED> { | 274 NUMERIC_RANGE_NOT_CONTAINED> { |
| 284 static constexpr RangeCheck Check(Src value) { | 275 static constexpr RangeCheck Check(Src value) { |
| 285 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 276 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 286 return RangeCheck(value <= DstLimits::max(), value >= DstLimits::lowest()); | 277 return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max()); |
| 287 } | 278 } |
| 288 }; | 279 }; |
| 289 | 280 |
| 290 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for | 281 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for |
| 291 // standard limits. | 282 // standard limits. |
| 292 template <typename Dst, typename Src, template <typename> class Bounds> | 283 template <typename Dst, typename Src, template <typename> class Bounds> |
| 293 struct DstRangeRelationToSrcRangeImpl<Dst, | 284 struct DstRangeRelationToSrcRangeImpl<Dst, |
| 294 Src, | 285 Src, |
| 295 Bounds, | 286 Bounds, |
| 296 INTEGER_REPRESENTATION_UNSIGNED, | 287 INTEGER_REPRESENTATION_UNSIGNED, |
| 297 INTEGER_REPRESENTATION_UNSIGNED, | 288 INTEGER_REPRESENTATION_UNSIGNED, |
| 298 NUMERIC_RANGE_NOT_CONTAINED> { | 289 NUMERIC_RANGE_NOT_CONTAINED> { |
| 299 static constexpr RangeCheck Check(Src value) { | 290 static constexpr RangeCheck Check(Src value) { |
| 300 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 291 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 301 return RangeCheck( | 292 return RangeCheck( |
| 302 value <= DstLimits::max(), | 293 DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(), |
| 303 DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest()); | 294 value <= DstLimits::max()); |
| 304 } | 295 } |
| 305 }; | 296 }; |
| 306 | 297 |
| 307 // Unsigned to signed: Only the upper bound can be exceeded for standard limits. | 298 // Unsigned to signed: Only the upper bound can be exceeded for standard limits. |
| 308 template <typename Dst, typename Src, template <typename> class Bounds> | 299 template <typename Dst, typename Src, template <typename> class Bounds> |
| 309 struct DstRangeRelationToSrcRangeImpl<Dst, | 300 struct DstRangeRelationToSrcRangeImpl<Dst, |
| 310 Src, | 301 Src, |
| 311 Bounds, | 302 Bounds, |
| 312 INTEGER_REPRESENTATION_SIGNED, | 303 INTEGER_REPRESENTATION_SIGNED, |
| 313 INTEGER_REPRESENTATION_UNSIGNED, | 304 INTEGER_REPRESENTATION_UNSIGNED, |
| 314 NUMERIC_RANGE_NOT_CONTAINED> { | 305 NUMERIC_RANGE_NOT_CONTAINED> { |
| 315 static constexpr RangeCheck Check(Src value) { | 306 static constexpr RangeCheck Check(Src value) { |
| 316 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 307 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 317 using Promotion = decltype(Src() + Dst()); | 308 using Promotion = decltype(Src() + Dst()); |
| 318 return RangeCheck(static_cast<Promotion>(value) <= | 309 return RangeCheck(DstLimits::lowest() <= Dst(0) || |
| 319 static_cast<Promotion>(DstLimits::max()), | |
| 320 DstLimits::lowest() <= Dst(0) || | |
| 321 static_cast<Promotion>(value) >= | 310 static_cast<Promotion>(value) >= |
| 322 static_cast<Promotion>(DstLimits::lowest())); | 311 static_cast<Promotion>(DstLimits::lowest()), |
| 312 static_cast<Promotion>(value) <= |
| 313 static_cast<Promotion>(DstLimits::max())); |
| 323 } | 314 } |
| 324 }; | 315 }; |
| 325 | 316 |
| 326 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, | 317 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, |
| 327 // and any negative value exceeds the lower boundary for standard limits. | 318 // and any negative value exceeds the lower boundary for standard limits. |
| 328 template <typename Dst, typename Src, template <typename> class Bounds> | 319 template <typename Dst, typename Src, template <typename> class Bounds> |
| 329 struct DstRangeRelationToSrcRangeImpl<Dst, | 320 struct DstRangeRelationToSrcRangeImpl<Dst, |
| 330 Src, | 321 Src, |
| 331 Bounds, | 322 Bounds, |
| 332 INTEGER_REPRESENTATION_UNSIGNED, | 323 INTEGER_REPRESENTATION_UNSIGNED, |
| 333 INTEGER_REPRESENTATION_SIGNED, | 324 INTEGER_REPRESENTATION_SIGNED, |
| 334 NUMERIC_RANGE_NOT_CONTAINED> { | 325 NUMERIC_RANGE_NOT_CONTAINED> { |
| 335 static constexpr RangeCheck Check(Src value) { | 326 static constexpr RangeCheck Check(Src value) { |
| 336 using SrcLimits = std::numeric_limits<Src>; | 327 using SrcLimits = std::numeric_limits<Src>; |
| 337 using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 328 using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 338 using Promotion = decltype(Src() + Dst()); | 329 using Promotion = decltype(Src() + Dst()); |
| 339 return RangeCheck( | 330 return RangeCheck( |
| 331 value >= Src(0) && (DstLimits::lowest() == 0 || |
| 332 static_cast<Dst>(value) >= DstLimits::lowest()), |
| 340 static_cast<Promotion>(SrcLimits::max()) <= | 333 static_cast<Promotion>(SrcLimits::max()) <= |
| 341 static_cast<Promotion>(DstLimits::max()) || | 334 static_cast<Promotion>(DstLimits::max()) || |
| 342 static_cast<Promotion>(value) <= | 335 static_cast<Promotion>(value) <= |
| 343 static_cast<Promotion>(DstLimits::max()), | 336 static_cast<Promotion>(DstLimits::max())); |
| 344 value >= Src(0) && (DstLimits::lowest() == 0 || | |
| 345 static_cast<Dst>(value) >= DstLimits::lowest())); | |
| 346 } | 337 } |
| 347 }; | 338 }; |
| 348 | 339 |
| 349 template <typename Dst, | 340 template <typename Dst, |
| 350 template <typename> class Bounds = std::numeric_limits, | 341 template <typename> class Bounds = std::numeric_limits, |
| 351 typename Src> | 342 typename Src> |
| 352 constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { | 343 constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { |
| 353 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); | 344 static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); |
| 354 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); | 345 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); |
| 355 static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); | 346 static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); |
| (...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 608 static const bool value = | 599 static const bool value = |
| 609 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && | 600 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && |
| 610 (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict); | 601 (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict); |
| 611 }; | 602 }; |
| 612 | 603 |
| 613 template <typename L, typename R> | 604 template <typename L, typename R> |
| 614 constexpr bool IsLessImpl(const L lhs, | 605 constexpr bool IsLessImpl(const L lhs, |
| 615 const R rhs, | 606 const R rhs, |
| 616 const RangeCheck l_range, | 607 const RangeCheck l_range, |
| 617 const RangeCheck r_range) { | 608 const RangeCheck r_range) { |
| 618 return l_range == RANGE_UNDERFLOW || r_range == RANGE_OVERFLOW || | 609 return l_range.IsUnderflow() || r_range.IsOverflow() || |
| 619 (l_range == r_range && | 610 (l_range == r_range && |
| 620 static_cast<decltype(lhs + rhs)>(lhs) < | 611 static_cast<decltype(lhs + rhs)>(lhs) < |
| 621 static_cast<decltype(lhs + rhs)>(rhs)); | 612 static_cast<decltype(lhs + rhs)>(rhs)); |
| 622 } | 613 } |
| 623 | 614 |
| 624 template <typename L, typename R> | 615 template <typename L, typename R> |
| 625 struct IsLess { | 616 struct IsLess { |
| 626 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, | 617 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 627 "Types must be numeric."); | 618 "Types must be numeric."); |
| 628 static constexpr bool Test(const L lhs, const R rhs) { | 619 static constexpr bool Test(const L lhs, const R rhs) { |
| 629 return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), | 620 return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
| 630 DstRangeRelationToSrcRange<L>(rhs)); | 621 DstRangeRelationToSrcRange<L>(rhs)); |
| 631 } | 622 } |
| 632 }; | 623 }; |
| 633 | 624 |
| 634 template <typename L, typename R> | 625 template <typename L, typename R> |
| 635 constexpr bool IsLessOrEqualImpl(const L lhs, | 626 constexpr bool IsLessOrEqualImpl(const L lhs, |
| 636 const R rhs, | 627 const R rhs, |
| 637 const RangeCheck l_range, | 628 const RangeCheck l_range, |
| 638 const RangeCheck r_range) { | 629 const RangeCheck r_range) { |
| 639 return l_range == RANGE_UNDERFLOW || r_range == RANGE_OVERFLOW || | 630 return l_range.IsUnderflow() || r_range.IsOverflow() || |
| 640 (l_range == r_range && | 631 (l_range == r_range && |
| 641 static_cast<decltype(lhs + rhs)>(lhs) <= | 632 static_cast<decltype(lhs + rhs)>(lhs) <= |
| 642 static_cast<decltype(lhs + rhs)>(rhs)); | 633 static_cast<decltype(lhs + rhs)>(rhs)); |
| 643 } | 634 } |
| 644 | 635 |
| 645 template <typename L, typename R> | 636 template <typename L, typename R> |
| 646 struct IsLessOrEqual { | 637 struct IsLessOrEqual { |
| 647 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, | 638 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 648 "Types must be numeric."); | 639 "Types must be numeric."); |
| 649 static constexpr bool Test(const L lhs, const R rhs) { | 640 static constexpr bool Test(const L lhs, const R rhs) { |
| 650 return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), | 641 return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
| 651 DstRangeRelationToSrcRange<L>(rhs)); | 642 DstRangeRelationToSrcRange<L>(rhs)); |
| 652 } | 643 } |
| 653 }; | 644 }; |
| 654 | 645 |
| 655 template <typename L, typename R> | 646 template <typename L, typename R> |
| 656 constexpr bool IsGreaterImpl(const L lhs, | 647 constexpr bool IsGreaterImpl(const L lhs, |
| 657 const R rhs, | 648 const R rhs, |
| 658 const RangeCheck l_range, | 649 const RangeCheck l_range, |
| 659 const RangeCheck r_range) { | 650 const RangeCheck r_range) { |
| 660 return l_range == RANGE_OVERFLOW || r_range == RANGE_UNDERFLOW || | 651 return l_range.IsOverflow() || r_range.IsUnderflow() || |
| 661 (l_range == r_range && | 652 (l_range == r_range && |
| 662 static_cast<decltype(lhs + rhs)>(lhs) > | 653 static_cast<decltype(lhs + rhs)>(lhs) > |
| 663 static_cast<decltype(lhs + rhs)>(rhs)); | 654 static_cast<decltype(lhs + rhs)>(rhs)); |
| 664 } | 655 } |
| 665 | 656 |
| 666 template <typename L, typename R> | 657 template <typename L, typename R> |
| 667 struct IsGreater { | 658 struct IsGreater { |
| 668 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, | 659 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 669 "Types must be numeric."); | 660 "Types must be numeric."); |
| 670 static constexpr bool Test(const L lhs, const R rhs) { | 661 static constexpr bool Test(const L lhs, const R rhs) { |
| 671 return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), | 662 return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
| 672 DstRangeRelationToSrcRange<L>(rhs)); | 663 DstRangeRelationToSrcRange<L>(rhs)); |
| 673 } | 664 } |
| 674 }; | 665 }; |
| 675 | 666 |
| 676 template <typename L, typename R> | 667 template <typename L, typename R> |
| 677 constexpr bool IsGreaterOrEqualImpl(const L lhs, | 668 constexpr bool IsGreaterOrEqualImpl(const L lhs, |
| 678 const R rhs, | 669 const R rhs, |
| 679 const RangeCheck l_range, | 670 const RangeCheck l_range, |
| 680 const RangeCheck r_range) { | 671 const RangeCheck r_range) { |
| 681 return l_range == RANGE_OVERFLOW || r_range == RANGE_UNDERFLOW || | 672 return l_range.IsOverflow() || r_range.IsUnderflow() || |
| 682 (l_range == r_range && | 673 (l_range == r_range && |
| 683 static_cast<decltype(lhs + rhs)>(lhs) >= | 674 static_cast<decltype(lhs + rhs)>(lhs) >= |
| 684 static_cast<decltype(lhs + rhs)>(rhs)); | 675 static_cast<decltype(lhs + rhs)>(rhs)); |
| 685 } | 676 } |
| 686 | 677 |
| 687 template <typename L, typename R> | 678 template <typename L, typename R> |
| 688 struct IsGreaterOrEqual { | 679 struct IsGreaterOrEqual { |
| 689 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, | 680 static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 690 "Types must be numeric."); | 681 "Types must be numeric."); |
| 691 static constexpr bool Test(const L lhs, const R rhs) { | 682 static constexpr bool Test(const L lhs, const R rhs) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 732 static_cast<BigType>(static_cast<L>(lhs)), | 723 static_cast<BigType>(static_cast<L>(lhs)), |
| 733 static_cast<BigType>(static_cast<R>(rhs))) | 724 static_cast<BigType>(static_cast<R>(rhs))) |
| 734 // Let the template functions figure it out for mixed types. | 725 // Let the template functions figure it out for mixed types. |
| 735 : C<L, R>::Test(lhs, rhs); | 726 : C<L, R>::Test(lhs, rhs); |
| 736 }; | 727 }; |
| 737 | 728 |
| 738 } // namespace internal | 729 } // namespace internal |
| 739 } // namespace base | 730 } // namespace base |
| 740 | 731 |
| 741 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | 732 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
| OLD | NEW |