Home | History | Annotate | Download | only in numerics
      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
      3 // found in the LICENSE file.
      4 
      5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_
      6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_
      7 
      8 #include <stddef.h>
      9 #include <stdint.h>
     10 
     11 #include <climits>
     12 #include <cmath>
     13 #include <cstdlib>
     14 #include <limits>
     15 #include <type_traits>
     16 
     17 #include "base/numerics/safe_conversions.h"
     18 
     19 namespace base {
     20 namespace internal {
     21 
     22 // Everything from here up to the floating point operations is portable C++,
     23 // but it may not be fast. This code could be split based on
     24 // platform/architecture and replaced with potentially faster implementations.
     25 
     26 // Integer promotion templates used by the portable checked integer arithmetic.
     27 template <size_t Size, bool IsSigned>
     28 struct IntegerForSizeAndSign;
     29 template <>
     30 struct IntegerForSizeAndSign<1, true> {
     31   typedef int8_t type;
     32 };
     33 template <>
     34 struct IntegerForSizeAndSign<1, false> {
     35   typedef uint8_t type;
     36 };
     37 template <>
     38 struct IntegerForSizeAndSign<2, true> {
     39   typedef int16_t type;
     40 };
     41 template <>
     42 struct IntegerForSizeAndSign<2, false> {
     43   typedef uint16_t type;
     44 };
     45 template <>
     46 struct IntegerForSizeAndSign<4, true> {
     47   typedef int32_t type;
     48 };
     49 template <>
     50 struct IntegerForSizeAndSign<4, false> {
     51   typedef uint32_t type;
     52 };
     53 template <>
     54 struct IntegerForSizeAndSign<8, true> {
     55   typedef int64_t type;
     56 };
     57 template <>
     58 struct IntegerForSizeAndSign<8, false> {
     59   typedef uint64_t type;
     60 };
     61 
     62 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
     63 // support 128-bit math, then the ArithmeticPromotion template below will need
     64 // to be updated (or more likely replaced with a decltype expression).
     65 
     66 template <typename Integer>
     67 struct UnsignedIntegerForSize {
     68   typedef typename std::enable_if<
     69       std::numeric_limits<Integer>::is_integer,
     70       typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type;
     71 };
     72 
     73 template <typename Integer>
     74 struct SignedIntegerForSize {
     75   typedef typename std::enable_if<
     76       std::numeric_limits<Integer>::is_integer,
     77       typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type;
     78 };
     79 
     80 template <typename Integer>
     81 struct TwiceWiderInteger {
     82   typedef typename std::enable_if<
     83       std::numeric_limits<Integer>::is_integer,
     84       typename IntegerForSizeAndSign<
     85           sizeof(Integer) * 2,
     86           std::numeric_limits<Integer>::is_signed>::type>::type type;
     87 };
     88 
     89 template <typename Integer>
     90 struct PositionOfSignBit {
     91   static const typename std::enable_if<std::numeric_limits<Integer>::is_integer,
     92                                        size_t>::type value =
     93       CHAR_BIT * sizeof(Integer) - 1;
     94 };
     95 
     96 // This is used for UnsignedAbs, where we need to support floating-point
     97 // template instantiations even though we don't actually support the operations.
     98 // However, there is no corresponding implementation of e.g. CheckedUnsignedAbs,
     99 // so the float versions will not compile.
    100 template <typename Numeric,
    101           bool IsInteger = std::numeric_limits<Numeric>::is_integer,
    102           bool IsFloat = std::numeric_limits<Numeric>::is_iec559>
    103 struct UnsignedOrFloatForSize;
    104 
    105 template <typename Numeric>
    106 struct UnsignedOrFloatForSize<Numeric, true, false> {
    107   typedef typename UnsignedIntegerForSize<Numeric>::type type;
    108 };
    109 
    110 template <typename Numeric>
    111 struct UnsignedOrFloatForSize<Numeric, false, true> {
    112   typedef Numeric type;
    113 };
    114 
    115 // Helper templates for integer manipulations.
    116 
    117 template <typename T>
    118 constexpr bool HasSignBit(T x) {
    119   // Cast to unsigned since right shift on signed is undefined.
    120   return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
    121             PositionOfSignBit<T>::value);
    122 }
    123 
    124 // This wrapper undoes the standard integer promotions.
    125 template <typename T>
    126 constexpr T BinaryComplement(T x) {
    127   return static_cast<T>(~x);
    128 }
    129 
    130 // 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
    132 // way to coalesce things into the CheckedNumericState specializations below.
    133 
    134 template <typename T>
    135 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
    136 CheckedAdd(T x, T y, RangeConstraint* validity) {
    137   // 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.
    139   typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
    140   UnsignedDst ux = static_cast<UnsignedDst>(x);
    141   UnsignedDst uy = static_cast<UnsignedDst>(y);
    142   UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
    143   // Addition is valid if the sign of (x + y) is equal to either that of x or
    144   // that of y.
    145   if (std::numeric_limits<T>::is_signed) {
    146     if (HasSignBit(BinaryComplement(
    147             static_cast<UnsignedDst>((uresult ^ ux) & (uresult ^ uy))))) {
    148       *validity = RANGE_VALID;
    149     } else {  // Direction of wrap is inverse of result sign.
    150       *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
    151     }
    152   } else {  // Unsigned is either valid or overflow.
    153     *validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW;
    154   }
    155   return static_cast<T>(uresult);
    156 }
    157 
    158 template <typename T>
    159 typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type
    160 CheckedSub(T x, T y, RangeConstraint* validity) {
    161   // Since the value of x+y is undefined if we have a signed type, we compute
    162   // it using the unsigned type of the same size.
    163   typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
    164   UnsignedDst ux = static_cast<UnsignedDst>(x);
    165   UnsignedDst uy = static_cast<UnsignedDst>(y);
    166   UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
    167   // Subtraction is valid if either x and y have same sign, or (x-y) and x have
    168   // the same sign.
    169   if (std::numeric_limits<T>::is_signed) {
    170     if (HasSignBit(BinaryComplement(
    171             static_cast<UnsignedDst>((uresult ^ ux) & (ux ^ uy))))) {
    172       *validity = RANGE_VALID;
    173     } else {  // Direction of wrap is inverse of result sign.
    174       *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
    175     }
    176   } else {  // Unsigned is either valid or underflow.
    177     *validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW;
    178   }
    179   return static_cast<T>(uresult);
    180 }
    181 
    182 // Integer multiplication is a bit complicated. In the fast case we just
    183 // we just promote to a twice wider type, and range check the result. In the
    184 // slow case we need to manually check that the result won't be truncated by
    185 // checking with division against the appropriate bound.
    186 template <typename T>
    187 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    188                             sizeof(T) * 2 <= sizeof(uintmax_t),
    189                         T>::type
    190 CheckedMul(T x, T y, RangeConstraint* validity) {
    191   typedef typename TwiceWiderInteger<T>::type IntermediateType;
    192   IntermediateType tmp =
    193       static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
    194   *validity = DstRangeRelationToSrcRange<T>(tmp);
    195   return static_cast<T>(tmp);
    196 }
    197 
    198 template <typename T>
    199 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    200                             std::numeric_limits<T>::is_signed &&
    201                             (sizeof(T) * 2 > sizeof(uintmax_t)),
    202                         T>::type
    203 CheckedMul(T x, T y, RangeConstraint* validity) {
    204   // If either side is zero then the result will be zero.
    205   if (!x || !y) {
    206     *validity = RANGE_VALID;
    207     return static_cast<T>(0);
    208 
    209   } else if (x > 0) {
    210     if (y > 0)
    211       *validity =
    212           x <= std::numeric_limits<T>::max() / y ? RANGE_VALID : RANGE_OVERFLOW;
    213     else
    214       *validity = y >= std::numeric_limits<T>::min() / x ? RANGE_VALID
    215                                                          : RANGE_UNDERFLOW;
    216 
    217   } else {
    218     if (y > 0)
    219       *validity = x >= std::numeric_limits<T>::min() / y ? RANGE_VALID
    220                                                          : RANGE_UNDERFLOW;
    221     else
    222       *validity =
    223           y >= std::numeric_limits<T>::max() / x ? RANGE_VALID : RANGE_OVERFLOW;
    224   }
    225 
    226   return static_cast<T>(x * y);
    227 }
    228 
    229 template <typename T>
    230 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    231                             !std::numeric_limits<T>::is_signed &&
    232                             (sizeof(T) * 2 > sizeof(uintmax_t)),
    233                         T>::type
    234 CheckedMul(T x, T y, RangeConstraint* validity) {
    235   *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y)
    236                   ? RANGE_VALID
    237                   : RANGE_OVERFLOW;
    238   return static_cast<T>(x * y);
    239 }
    240 
    241 // Division just requires a check for an invalid negation on signed min/-1.
    242 template <typename T>
    243 T CheckedDiv(T x,
    244              T y,
    245              RangeConstraint* validity,
    246              typename std::enable_if<std::numeric_limits<T>::is_integer,
    247                                      int>::type = 0) {
    248   if (std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() &&
    249       y == static_cast<T>(-1)) {
    250     *validity = RANGE_OVERFLOW;
    251     return std::numeric_limits<T>::min();
    252   }
    253 
    254   *validity = RANGE_VALID;
    255   return static_cast<T>(x / y);
    256 }
    257 
    258 template <typename T>
    259 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    260                             std::numeric_limits<T>::is_signed,
    261                         T>::type
    262 CheckedMod(T x, T y, RangeConstraint* validity) {
    263   *validity = y > 0 ? RANGE_VALID : RANGE_INVALID;
    264   return static_cast<T>(x % y);
    265 }
    266 
    267 template <typename T>
    268 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    269                             !std::numeric_limits<T>::is_signed,
    270                         T>::type
    271 CheckedMod(T x, T y, RangeConstraint* validity) {
    272   *validity = RANGE_VALID;
    273   return static_cast<T>(x % y);
    274 }
    275 
    276 template <typename T>
    277 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    278                             std::numeric_limits<T>::is_signed,
    279                         T>::type
    280 CheckedNeg(T value, RangeConstraint* validity) {
    281   *validity =
    282       value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
    283   // The negation of signed min is min, so catch that one.
    284   return static_cast<T>(-value);
    285 }
    286 
    287 template <typename T>
    288 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    289                             !std::numeric_limits<T>::is_signed,
    290                         T>::type
    291 CheckedNeg(T value, RangeConstraint* validity) {
    292   // The only legal unsigned negation is zero.
    293   *validity = value ? RANGE_UNDERFLOW : RANGE_VALID;
    294   return static_cast<T>(
    295       -static_cast<typename SignedIntegerForSize<T>::type>(value));
    296 }
    297 
    298 template <typename T>
    299 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    300                             std::numeric_limits<T>::is_signed,
    301                         T>::type
    302 CheckedAbs(T value, RangeConstraint* validity) {
    303   *validity =
    304       value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
    305   return static_cast<T>(std::abs(value));
    306 }
    307 
    308 template <typename T>
    309 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    310                             !std::numeric_limits<T>::is_signed,
    311                         T>::type
    312 CheckedAbs(T value, RangeConstraint* validity) {
    313   // T is unsigned, so |value| must already be positive.
    314   *validity = RANGE_VALID;
    315   return value;
    316 }
    317 
    318 template <typename T>
    319 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    320                             std::numeric_limits<T>::is_signed,
    321                         typename UnsignedIntegerForSize<T>::type>::type
    322 CheckedUnsignedAbs(T value) {
    323   typedef typename UnsignedIntegerForSize<T>::type UnsignedT;
    324   return value == std::numeric_limits<T>::min()
    325              ? static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1
    326              : static_cast<UnsignedT>(std::abs(value));
    327 }
    328 
    329 template <typename T>
    330 typename std::enable_if<std::numeric_limits<T>::is_integer &&
    331                             !std::numeric_limits<T>::is_signed,
    332                         T>::type
    333 CheckedUnsignedAbs(T value) {
    334   // T is unsigned, so |value| must already be positive.
    335   return static_cast<T>(value);
    336 }
    337 
    338 // These are the floating point stubs that the compiler needs to see. Only the
    339 // negation operation is ever called.
    340 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME)                             \
    341   template <typename T>                                               \
    342   typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type \
    343       Checked##NAME(T, T, RangeConstraint*) {                         \
    344     NOTREACHED();                                                     \
    345     return static_cast<T>(0);                                         \
    346   }
    347 
    348 BASE_FLOAT_ARITHMETIC_STUBS(Add)
    349 BASE_FLOAT_ARITHMETIC_STUBS(Sub)
    350 BASE_FLOAT_ARITHMETIC_STUBS(Mul)
    351 BASE_FLOAT_ARITHMETIC_STUBS(Div)
    352 BASE_FLOAT_ARITHMETIC_STUBS(Mod)
    353 
    354 #undef BASE_FLOAT_ARITHMETIC_STUBS
    355 
    356 template <typename T>
    357 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg(
    358     T value,
    359     RangeConstraint*) {
    360   return static_cast<T>(-value);
    361 }
    362 
    363 template <typename T>
    364 typename std::enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(
    365     T value,
    366     RangeConstraint*) {
    367   return static_cast<T>(std::abs(value));
    368 }
    369 
    370 // Floats carry around their validity state with them, but integers do not. So,
    371 // we wrap the underlying value in a specialization in order to hide that detail
    372 // and expose an interface via accessors.
    373 enum NumericRepresentation {
    374   NUMERIC_INTEGER,
    375   NUMERIC_FLOATING,
    376   NUMERIC_UNKNOWN
    377 };
    378 
    379 template <typename NumericType>
    380 struct GetNumericRepresentation {
    381   static const NumericRepresentation value =
    382       std::numeric_limits<NumericType>::is_integer
    383           ? NUMERIC_INTEGER
    384           : (std::numeric_limits<NumericType>::is_iec559 ? NUMERIC_FLOATING
    385                                                          : NUMERIC_UNKNOWN);
    386 };
    387 
    388 template <typename T, NumericRepresentation type =
    389                           GetNumericRepresentation<T>::value>
    390 class CheckedNumericState {};
    391 
    392 // Integrals require quite a bit of additional housekeeping to manage state.
    393 template <typename T>
    394 class CheckedNumericState<T, NUMERIC_INTEGER> {
    395  private:
    396   T value_;
    397   RangeConstraint validity_ : CHAR_BIT;  // Actually requires only two bits.
    398 
    399  public:
    400   template <typename Src, NumericRepresentation type>
    401   friend class CheckedNumericState;
    402 
    403   CheckedNumericState() : value_(0), validity_(RANGE_VALID) {}
    404 
    405   template <typename Src>
    406   CheckedNumericState(Src value, RangeConstraint validity)
    407       : value_(static_cast<T>(value)),
    408         validity_(GetRangeConstraint(validity |
    409                                      DstRangeRelationToSrcRange<T>(value))) {
    410     static_assert(std::numeric_limits<Src>::is_specialized,
    411                   "Argument must be numeric.");
    412   }
    413 
    414   // Copy constructor.
    415   template <typename Src>
    416   CheckedNumericState(const CheckedNumericState<Src>& rhs)
    417       : value_(static_cast<T>(rhs.value())),
    418         validity_(GetRangeConstraint(
    419             rhs.validity() | DstRangeRelationToSrcRange<T>(rhs.value()))) {}
    420 
    421   template <typename Src>
    422   explicit CheckedNumericState(
    423       Src value,
    424       typename std::enable_if<std::numeric_limits<Src>::is_specialized,
    425                               int>::type = 0)
    426       : value_(static_cast<T>(value)),
    427         validity_(DstRangeRelationToSrcRange<T>(value)) {}
    428 
    429   RangeConstraint validity() const { return validity_; }
    430   T value() const { return value_; }
    431 };
    432 
    433 // Floating points maintain their own validity, but need translation wrappers.
    434 template <typename T>
    435 class CheckedNumericState<T, NUMERIC_FLOATING> {
    436  private:
    437   T value_;
    438 
    439  public:
    440   template <typename Src, NumericRepresentation type>
    441   friend class CheckedNumericState;
    442 
    443   CheckedNumericState() : value_(0.0) {}
    444 
    445   template <typename Src>
    446   CheckedNumericState(
    447       Src value,
    448       RangeConstraint /*validity*/,
    449       typename std::enable_if<std::numeric_limits<Src>::is_integer, int>::type =
    450           0) {
    451     switch (DstRangeRelationToSrcRange<T>(value)) {
    452       case RANGE_VALID:
    453         value_ = static_cast<T>(value);
    454         break;
    455 
    456       case RANGE_UNDERFLOW:
    457         value_ = -std::numeric_limits<T>::infinity();
    458         break;
    459 
    460       case RANGE_OVERFLOW:
    461         value_ = std::numeric_limits<T>::infinity();
    462         break;
    463 
    464       case RANGE_INVALID:
    465         value_ = std::numeric_limits<T>::quiet_NaN();
    466         break;
    467 
    468       default:
    469         NOTREACHED();
    470     }
    471   }
    472 
    473   template <typename Src>
    474   explicit CheckedNumericState(
    475       Src value,
    476       typename std::enable_if<std::numeric_limits<Src>::is_specialized,
    477                               int>::type = 0)
    478       : value_(static_cast<T>(value)) {}
    479 
    480   // Copy constructor.
    481   template <typename Src>
    482   CheckedNumericState(const CheckedNumericState<Src>& rhs)
    483       : value_(static_cast<T>(rhs.value())) {}
    484 
    485   RangeConstraint validity() const {
    486     return GetRangeConstraint(value_ <= std::numeric_limits<T>::max(),
    487                               value_ >= -std::numeric_limits<T>::max());
    488   }
    489   T value() const { return value_; }
    490 };
    491 
    492 // For integers less than 128-bit and floats 32-bit or larger, we have the type
    493 // with the larger maximum exponent take precedence.
    494 enum ArithmeticPromotionCategory { LEFT_PROMOTION, RIGHT_PROMOTION };
    495 
    496 template <typename Lhs,
    497           typename Rhs = Lhs,
    498           ArithmeticPromotionCategory Promotion =
    499               (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
    500                   ? LEFT_PROMOTION
    501                   : RIGHT_PROMOTION>
    502 struct ArithmeticPromotion;
    503 
    504 template <typename Lhs, typename Rhs>
    505 struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> {
    506   typedef Lhs type;
    507 };
    508 
    509 template <typename Lhs, typename Rhs>
    510 struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
    511   typedef Rhs type;
    512 };
    513 
    514 // We can statically check if operations on the provided types can wrap, so we
    515 // can skip the checked operations if they're not needed. So, for an integer we
    516 // care if the destination type preserves the sign and is twice the width of
    517 // the source.
    518 template <typename T, typename Lhs, typename Rhs>
    519 struct IsIntegerArithmeticSafe {
    520   static const bool value = !std::numeric_limits<T>::is_iec559 &&
    521                             StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
    522                                 NUMERIC_RANGE_CONTAINED &&
    523                             sizeof(T) >= (2 * sizeof(Lhs)) &&
    524                             StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
    525                                 NUMERIC_RANGE_CONTAINED &&
    526                             sizeof(T) >= (2 * sizeof(Rhs));
    527 };
    528 
    529 }  // namespace internal
    530 }  // namespace base
    531 
    532 #endif  // BASE_NUMERICS_SAFE_MATH_IMPL_H_
    533