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_CONVERSIONS_IMPL_H_
      6 #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
      7 
      8 #include <limits.h>
      9 #include <stdint.h>
     10 
     11 #include <limits>
     12 
     13 namespace base {
     14 namespace internal {
     15 
     16 // The std library doesn't provide a binary max_exponent for integers, however
     17 // we can compute one by adding one to the number of non-sign bits. This allows
     18 // for accurate range comparisons between floating point and integer types.
     19 template <typename NumericType>
     20 struct MaxExponent {
     21   static const int value = std::numeric_limits<NumericType>::is_iec559
     22                                ? std::numeric_limits<NumericType>::max_exponent
     23                                : (sizeof(NumericType) * 8 + 1 -
     24                                   std::numeric_limits<NumericType>::is_signed);
     25 };
     26 
     27 enum IntegerRepresentation {
     28   INTEGER_REPRESENTATION_UNSIGNED,
     29   INTEGER_REPRESENTATION_SIGNED
     30 };
     31 
     32 // A range for a given nunmeric Src type is contained for a given numeric Dst
     33 // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
     34 // numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true.
     35 // We implement this as template specializations rather than simple static
     36 // comparisons to ensure type correctness in our comparisons.
     37 enum NumericRangeRepresentation {
     38   NUMERIC_RANGE_NOT_CONTAINED,
     39   NUMERIC_RANGE_CONTAINED
     40 };
     41 
     42 // Helper templates to statically determine if our destination type can contain
     43 // maximum and minimum values represented by the source type.
     44 
     45 template <
     46     typename Dst,
     47     typename Src,
     48     IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
     49                                             ? INTEGER_REPRESENTATION_SIGNED
     50                                             : INTEGER_REPRESENTATION_UNSIGNED,
     51     IntegerRepresentation SrcSign =
     52         std::numeric_limits<Src>::is_signed
     53             ? INTEGER_REPRESENTATION_SIGNED
     54             : INTEGER_REPRESENTATION_UNSIGNED >
     55 struct StaticDstRangeRelationToSrcRange;
     56 
     57 // Same sign: Dst is guaranteed to contain Src only if its range is equal or
     58 // larger.
     59 template <typename Dst, typename Src, IntegerRepresentation Sign>
     60 struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
     61   static const NumericRangeRepresentation value =
     62       MaxExponent<Dst>::value >= MaxExponent<Src>::value
     63           ? NUMERIC_RANGE_CONTAINED
     64           : NUMERIC_RANGE_NOT_CONTAINED;
     65 };
     66 
     67 // Unsigned to signed: Dst is guaranteed to contain source only if its range is
     68 // larger.
     69 template <typename Dst, typename Src>
     70 struct StaticDstRangeRelationToSrcRange<Dst,
     71                                         Src,
     72                                         INTEGER_REPRESENTATION_SIGNED,
     73                                         INTEGER_REPRESENTATION_UNSIGNED> {
     74   static const NumericRangeRepresentation value =
     75       MaxExponent<Dst>::value > MaxExponent<Src>::value
     76           ? NUMERIC_RANGE_CONTAINED
     77           : NUMERIC_RANGE_NOT_CONTAINED;
     78 };
     79 
     80 // Signed to unsigned: Dst cannot be statically determined to contain Src.
     81 template <typename Dst, typename Src>
     82 struct StaticDstRangeRelationToSrcRange<Dst,
     83                                         Src,
     84                                         INTEGER_REPRESENTATION_UNSIGNED,
     85                                         INTEGER_REPRESENTATION_SIGNED> {
     86   static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
     87 };
     88 
     89 enum RangeConstraint {
     90   RANGE_VALID = 0x0,  // Value can be represented by the destination type.
     91   RANGE_UNDERFLOW = 0x1,  // Value would overflow.
     92   RANGE_OVERFLOW = 0x2,  // Value would underflow.
     93   RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW  // Invalid (i.e. NaN).
     94 };
     95 
     96 // Helper function for coercing an int back to a RangeContraint.
     97 inline RangeConstraint GetRangeConstraint(int integer_range_constraint) {
     98   DCHECK(integer_range_constraint >= RANGE_VALID &&
     99          integer_range_constraint <= RANGE_INVALID);
    100   return static_cast<RangeConstraint>(integer_range_constraint);
    101 }
    102 
    103 // This function creates a RangeConstraint from an upper and lower bound
    104 // check by taking advantage of the fact that only NaN can be out of range in
    105 // both directions at once.
    106 inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound,
    107                                    bool is_in_lower_bound) {
    108   return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) |
    109                             (is_in_lower_bound ? 0 : RANGE_UNDERFLOW));
    110 }
    111 
    112 // The following helper template addresses a corner case in range checks for
    113 // conversion from a floating-point type to an integral type of smaller range
    114 // but larger precision (e.g. float -> unsigned). The problem is as follows:
    115 //   1. Integral maximum is always one less than a power of two, so it must be
    116 //      truncated to fit the mantissa of the floating point. The direction of
    117 //      rounding is implementation defined, but by default it's always IEEE
    118 //      floats, which round to nearest and thus result in a value of larger
    119 //      magnitude than the integral value.
    120 //      Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
    121 //                                   // is 4294967295u.
    122 //   2. If the floating point value is equal to the promoted integral maximum
    123 //      value, a range check will erroneously pass.
    124 //      Example: (4294967296f <= 4294967295u) // This is true due to a precision
    125 //                                            // loss in rounding up to float.
    126 //   3. When the floating point value is then converted to an integral, the
    127 //      resulting value is out of range for the target integral type and
    128 //      thus is implementation defined.
    129 //      Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
    130 // To fix this bug we manually truncate the maximum value when the destination
    131 // type is an integral of larger precision than the source floating-point type,
    132 // such that the resulting maximum is represented exactly as a floating point.
    133 template <typename Dst, typename Src>
    134 struct NarrowingRange {
    135   typedef typename std::numeric_limits<Src> SrcLimits;
    136   typedef typename std::numeric_limits<Dst> DstLimits;
    137 
    138   static Dst max() {
    139     // The following logic avoids warnings where the max function is
    140     // instantiated with invalid values for a bit shift (even though
    141     // such a function can never be called).
    142     static const int shift =
    143         (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
    144          SrcLimits::digits < DstLimits::digits && SrcLimits::is_iec559 &&
    145          DstLimits::is_integer)
    146             ? (DstLimits::digits - SrcLimits::digits)
    147             : 0;
    148 
    149     // We use UINTMAX_C below to avoid compiler warnings about shifting floating
    150     // points. Since it's a compile time calculation, it shouldn't have any
    151     // performance impact.
    152     return DstLimits::max() - static_cast<Dst>((UINTMAX_C(1) << shift) - 1);
    153   }
    154 
    155   static Dst min() {
    156     return std::numeric_limits<Dst>::is_iec559 ? -DstLimits::max()
    157                                                : DstLimits::min();
    158   }
    159 };
    160 
    161 template <
    162     typename Dst,
    163     typename Src,
    164     IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
    165                                             ? INTEGER_REPRESENTATION_SIGNED
    166                                             : INTEGER_REPRESENTATION_UNSIGNED,
    167     IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed
    168                                             ? INTEGER_REPRESENTATION_SIGNED
    169                                             : INTEGER_REPRESENTATION_UNSIGNED,
    170     NumericRangeRepresentation DstRange =
    171         StaticDstRangeRelationToSrcRange<Dst, Src>::value >
    172 struct DstRangeRelationToSrcRangeImpl;
    173 
    174 // The following templates are for ranges that must be verified at runtime. We
    175 // split it into checks based on signedness to avoid confusing casts and
    176 // compiler warnings on signed an unsigned comparisons.
    177 
    178 // Dst range is statically determined to contain Src: Nothing to check.
    179 template <typename Dst,
    180           typename Src,
    181           IntegerRepresentation DstSign,
    182           IntegerRepresentation SrcSign>
    183 struct DstRangeRelationToSrcRangeImpl<Dst,
    184                                       Src,
    185                                       DstSign,
    186                                       SrcSign,
    187                                       NUMERIC_RANGE_CONTAINED> {
    188   static RangeConstraint Check(Src /* value */) { return RANGE_VALID; }
    189 };
    190 
    191 // Signed to signed narrowing: Both the upper and lower boundaries may be
    192 // exceeded.
    193 template <typename Dst, typename Src>
    194 struct DstRangeRelationToSrcRangeImpl<Dst,
    195                                       Src,
    196                                       INTEGER_REPRESENTATION_SIGNED,
    197                                       INTEGER_REPRESENTATION_SIGNED,
    198                                       NUMERIC_RANGE_NOT_CONTAINED> {
    199   static RangeConstraint Check(Src value) {
    200     return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()),
    201                               (value >= NarrowingRange<Dst, Src>::min()));
    202   }
    203 };
    204 
    205 // Unsigned to unsigned narrowing: Only the upper boundary can be exceeded.
    206 template <typename Dst, typename Src>
    207 struct DstRangeRelationToSrcRangeImpl<Dst,
    208                                       Src,
    209                                       INTEGER_REPRESENTATION_UNSIGNED,
    210                                       INTEGER_REPRESENTATION_UNSIGNED,
    211                                       NUMERIC_RANGE_NOT_CONTAINED> {
    212   static RangeConstraint Check(Src value) {
    213     return GetRangeConstraint(value <= NarrowingRange<Dst, Src>::max(), true);
    214   }
    215 };
    216 
    217 // Unsigned to signed: The upper boundary may be exceeded.
    218 template <typename Dst, typename Src>
    219 struct DstRangeRelationToSrcRangeImpl<Dst,
    220                                       Src,
    221                                       INTEGER_REPRESENTATION_SIGNED,
    222                                       INTEGER_REPRESENTATION_UNSIGNED,
    223                                       NUMERIC_RANGE_NOT_CONTAINED> {
    224   static RangeConstraint Check(Src value) {
    225     return sizeof(Dst) > sizeof(Src)
    226                ? RANGE_VALID
    227                : GetRangeConstraint(
    228                      value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
    229                      true);
    230   }
    231 };
    232 
    233 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
    234 // and any negative value exceeds the lower boundary.
    235 template <typename Dst, typename Src>
    236 struct DstRangeRelationToSrcRangeImpl<Dst,
    237                                       Src,
    238                                       INTEGER_REPRESENTATION_UNSIGNED,
    239                                       INTEGER_REPRESENTATION_SIGNED,
    240                                       NUMERIC_RANGE_NOT_CONTAINED> {
    241   static RangeConstraint Check(Src value) {
    242     return (MaxExponent<Dst>::value >= MaxExponent<Src>::value)
    243                ? GetRangeConstraint(true, value >= static_cast<Src>(0))
    244                : GetRangeConstraint(
    245                      value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
    246                      value >= static_cast<Src>(0));
    247   }
    248 };
    249 
    250 template <typename Dst, typename Src>
    251 inline RangeConstraint DstRangeRelationToSrcRange(Src value) {
    252   static_assert(std::numeric_limits<Src>::is_specialized,
    253                 "Argument must be numeric.");
    254   static_assert(std::numeric_limits<Dst>::is_specialized,
    255                 "Result must be numeric.");
    256   return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value);
    257 }
    258 
    259 }  // namespace internal
    260 }  // namespace base
    261 
    262 #endif  // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
    263