blob: 35b2692f921fae2dd4b7d43a1013f5902454a8f4 [file] [log] [blame]
[email protected]4efb2c32014-01-16 06:57:251// 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
danakj0a448602015-03-10 00:31:165#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
6#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
[email protected]4efb2c32014-01-16 06:57:257
avi9b6f42932015-12-26 22:15:148#include <limits.h>
9#include <stdint.h>
10
rickyzd2fe3e242016-05-10 13:47:3711#include <limits>
12
[email protected]4efb2c32014-01-16 06:57:2513namespace base {
14namespace internal {
15
[email protected]5bfecbc52014-02-27 13:49:0416// 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.
19template <typename NumericType>
20struct MaxExponent {
rickyzd2fe3e242016-05-10 13:47:3721 static_assert(std::is_arithmetic<NumericType>::value,
22 "Argument must be numeric.");
[email protected]5bfecbc52014-02-27 13:49:0423 static const int value = std::numeric_limits<NumericType>::is_iec559
24 ? std::numeric_limits<NumericType>::max_exponent
25 : (sizeof(NumericType) * 8 + 1 -
26 std::numeric_limits<NumericType>::is_signed);
[email protected]4efb2c32014-01-16 06:57:2527};
28
[email protected]5bfecbc52014-02-27 13:49:0429enum IntegerRepresentation {
30 INTEGER_REPRESENTATION_UNSIGNED,
31 INTEGER_REPRESENTATION_SIGNED
[email protected]4efb2c32014-01-16 06:57:2532};
33
[email protected]5bfecbc52014-02-27 13:49:0434// A range for a given nunmeric Src type is contained for a given numeric Dst
35// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
36// numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true.
37// We implement this as template specializations rather than simple static
38// comparisons to ensure type correctness in our comparisons.
39enum NumericRangeRepresentation {
40 NUMERIC_RANGE_NOT_CONTAINED,
41 NUMERIC_RANGE_CONTAINED
[email protected]4efb2c32014-01-16 06:57:2542};
43
44// Helper templates to statically determine if our destination type can contain
[email protected]5bfecbc52014-02-27 13:49:0445// maximum and minimum values represented by the source type.
[email protected]4efb2c32014-01-16 06:57:2546
[email protected]5bfecbc52014-02-27 13:49:0447template <
48 typename Dst,
49 typename Src,
50 IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
51 ? INTEGER_REPRESENTATION_SIGNED
52 : INTEGER_REPRESENTATION_UNSIGNED,
53 IntegerRepresentation SrcSign =
54 std::numeric_limits<Src>::is_signed
55 ? INTEGER_REPRESENTATION_SIGNED
56 : INTEGER_REPRESENTATION_UNSIGNED >
57struct StaticDstRangeRelationToSrcRange;
[email protected]4efb2c32014-01-16 06:57:2558
[email protected]5bfecbc52014-02-27 13:49:0459// Same sign: Dst is guaranteed to contain Src only if its range is equal or
60// larger.
61template <typename Dst, typename Src, IntegerRepresentation Sign>
62struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
63 static const NumericRangeRepresentation value =
64 MaxExponent<Dst>::value >= MaxExponent<Src>::value
65 ? NUMERIC_RANGE_CONTAINED
66 : NUMERIC_RANGE_NOT_CONTAINED;
67};
68
69// Unsigned to signed: Dst is guaranteed to contain source only if its range is
70// larger.
[email protected]4efb2c32014-01-16 06:57:2571template <typename Dst, typename Src>
[email protected]5bfecbc52014-02-27 13:49:0472struct StaticDstRangeRelationToSrcRange<Dst,
73 Src,
74 INTEGER_REPRESENTATION_SIGNED,
75 INTEGER_REPRESENTATION_UNSIGNED> {
76 static const NumericRangeRepresentation value =
77 MaxExponent<Dst>::value > MaxExponent<Src>::value
78 ? NUMERIC_RANGE_CONTAINED
79 : NUMERIC_RANGE_NOT_CONTAINED;
[email protected]4efb2c32014-01-16 06:57:2580};
81
[email protected]5bfecbc52014-02-27 13:49:0482// Signed to unsigned: Dst cannot be statically determined to contain Src.
[email protected]4efb2c32014-01-16 06:57:2583template <typename Dst, typename Src>
[email protected]5bfecbc52014-02-27 13:49:0484struct StaticDstRangeRelationToSrcRange<Dst,
85 Src,
86 INTEGER_REPRESENTATION_UNSIGNED,
87 INTEGER_REPRESENTATION_SIGNED> {
88 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
[email protected]4efb2c32014-01-16 06:57:2589};
90
[email protected]5bfecbc52014-02-27 13:49:0491enum RangeConstraint {
92 RANGE_VALID = 0x0, // Value can be represented by the destination type.
93 RANGE_UNDERFLOW = 0x1, // Value would overflow.
94 RANGE_OVERFLOW = 0x2, // Value would underflow.
95 RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN).
[email protected]4efb2c32014-01-16 06:57:2596};
97
[email protected]5bfecbc52014-02-27 13:49:0498// Helper function for coercing an int back to a RangeContraint.
jschuh83882392016-04-20 23:22:5399inline constexpr RangeConstraint GetRangeConstraint(
100 int integer_range_constraint) {
101 // TODO(jschuh): Once we get full C++14 support we want this
102 // assert(integer_range_constraint >= RANGE_VALID &&
103 // integer_range_constraint <= RANGE_INVALID)
[email protected]5bfecbc52014-02-27 13:49:04104 return static_cast<RangeConstraint>(integer_range_constraint);
105}
[email protected]4efb2c32014-01-16 06:57:25106
[email protected]5bfecbc52014-02-27 13:49:04107// This function creates a RangeConstraint from an upper and lower bound
[email protected]4efb2c32014-01-16 06:57:25108// check by taking advantage of the fact that only NaN can be out of range in
109// both directions at once.
jschuh83882392016-04-20 23:22:53110constexpr inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound,
111 bool is_in_lower_bound) {
[email protected]5bfecbc52014-02-27 13:49:04112 return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) |
113 (is_in_lower_bound ? 0 : RANGE_UNDERFLOW));
114}
[email protected]4efb2c32014-01-16 06:57:25115
jschuhfafe0712015-09-14 20:21:24116// The following helper template addresses a corner case in range checks for
117// conversion from a floating-point type to an integral type of smaller range
118// but larger precision (e.g. float -> unsigned). The problem is as follows:
119// 1. Integral maximum is always one less than a power of two, so it must be
120// truncated to fit the mantissa of the floating point. The direction of
121// rounding is implementation defined, but by default it's always IEEE
122// floats, which round to nearest and thus result in a value of larger
123// magnitude than the integral value.
124// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
125// // is 4294967295u.
126// 2. If the floating point value is equal to the promoted integral maximum
127// value, a range check will erroneously pass.
128// Example: (4294967296f <= 4294967295u) // This is true due to a precision
129// // loss in rounding up to float.
130// 3. When the floating point value is then converted to an integral, the
131// resulting value is out of range for the target integral type and
132// thus is implementation defined.
133// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
134// To fix this bug we manually truncate the maximum value when the destination
135// type is an integral of larger precision than the source floating-point type,
136// such that the resulting maximum is represented exactly as a floating point.
137template <typename Dst, typename Src>
138struct NarrowingRange {
139 typedef typename std::numeric_limits<Src> SrcLimits;
140 typedef typename std::numeric_limits<Dst> DstLimits;
jschuh83882392016-04-20 23:22:53141 // The following logic avoids warnings where the max function is
142 // instantiated with invalid values for a bit shift (even though
143 // such a function can never be called).
144 static const int shift = (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
145 SrcLimits::digits < DstLimits::digits &&
146 SrcLimits::is_iec559 &&
147 DstLimits::is_integer)
148 ? (DstLimits::digits - SrcLimits::digits)
149 : 0;
jschuhfafe0712015-09-14 20:21:24150
jschuh83882392016-04-20 23:22:53151 static constexpr Dst max() {
jschuhfafe0712015-09-14 20:21:24152 // We use UINTMAX_C below to avoid compiler warnings about shifting floating
153 // points. Since it's a compile time calculation, it shouldn't have any
154 // performance impact.
155 return DstLimits::max() - static_cast<Dst>((UINTMAX_C(1) << shift) - 1);
156 }
157
jschuh83882392016-04-20 23:22:53158 static constexpr Dst min() {
jschuhfafe0712015-09-14 20:21:24159 return std::numeric_limits<Dst>::is_iec559 ? -DstLimits::max()
160 : DstLimits::min();
161 }
162};
163
[email protected]5bfecbc52014-02-27 13:49:04164template <
165 typename Dst,
166 typename Src,
167 IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
168 ? INTEGER_REPRESENTATION_SIGNED
169 : INTEGER_REPRESENTATION_UNSIGNED,
170 IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed
171 ? INTEGER_REPRESENTATION_SIGNED
172 : INTEGER_REPRESENTATION_UNSIGNED,
173 NumericRangeRepresentation DstRange =
174 StaticDstRangeRelationToSrcRange<Dst, Src>::value >
175struct DstRangeRelationToSrcRangeImpl;
[email protected]4efb2c32014-01-16 06:57:25176
177// The following templates are for ranges that must be verified at runtime. We
178// split it into checks based on signedness to avoid confusing casts and
179// compiler warnings on signed an unsigned comparisons.
180
[email protected]5bfecbc52014-02-27 13:49:04181// Dst range is statically determined to contain Src: Nothing to check.
182template <typename Dst,
183 typename Src,
184 IntegerRepresentation DstSign,
185 IntegerRepresentation SrcSign>
186struct DstRangeRelationToSrcRangeImpl<Dst,
187 Src,
188 DstSign,
189 SrcSign,
190 NUMERIC_RANGE_CONTAINED> {
jschuh83882392016-04-20 23:22:53191 static constexpr RangeConstraint Check(Src value) { return RANGE_VALID; }
[email protected]5bfecbc52014-02-27 13:49:04192};
193
194// Signed to signed narrowing: Both the upper and lower boundaries may be
195// exceeded.
196template <typename Dst, typename Src>
197struct DstRangeRelationToSrcRangeImpl<Dst,
198 Src,
199 INTEGER_REPRESENTATION_SIGNED,
200 INTEGER_REPRESENTATION_SIGNED,
201 NUMERIC_RANGE_NOT_CONTAINED> {
jschuh83882392016-04-20 23:22:53202 static constexpr RangeConstraint Check(Src value) {
jschuhfafe0712015-09-14 20:21:24203 return GetRangeConstraint((value <= NarrowingRange<Dst, Src>::max()),
204 (value >= NarrowingRange<Dst, Src>::min()));
[email protected]4efb2c32014-01-16 06:57:25205 }
206};
207
[email protected]5bfecbc52014-02-27 13:49:04208// Unsigned to unsigned narrowing: Only the upper boundary can be exceeded.
[email protected]4efb2c32014-01-16 06:57:25209template <typename Dst, typename Src>
[email protected]5bfecbc52014-02-27 13:49:04210struct DstRangeRelationToSrcRangeImpl<Dst,
211 Src,
212 INTEGER_REPRESENTATION_UNSIGNED,
213 INTEGER_REPRESENTATION_UNSIGNED,
214 NUMERIC_RANGE_NOT_CONTAINED> {
jschuh83882392016-04-20 23:22:53215 static constexpr RangeConstraint Check(Src value) {
jschuhfafe0712015-09-14 20:21:24216 return GetRangeConstraint(value <= NarrowingRange<Dst, Src>::max(), true);
[email protected]4efb2c32014-01-16 06:57:25217 }
218};
219
[email protected]5bfecbc52014-02-27 13:49:04220// Unsigned to signed: The upper boundary may be exceeded.
[email protected]4efb2c32014-01-16 06:57:25221template <typename Dst, typename Src>
[email protected]5bfecbc52014-02-27 13:49:04222struct DstRangeRelationToSrcRangeImpl<Dst,
223 Src,
224 INTEGER_REPRESENTATION_SIGNED,
225 INTEGER_REPRESENTATION_UNSIGNED,
226 NUMERIC_RANGE_NOT_CONTAINED> {
jschuh83882392016-04-20 23:22:53227 static constexpr RangeConstraint Check(Src value) {
[email protected]5bfecbc52014-02-27 13:49:04228 return sizeof(Dst) > sizeof(Src)
229 ? RANGE_VALID
230 : GetRangeConstraint(
jschuhfafe0712015-09-14 20:21:24231 value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
[email protected]5bfecbc52014-02-27 13:49:04232 true);
[email protected]4efb2c32014-01-16 06:57:25233 }
234};
235
[email protected]5bfecbc52014-02-27 13:49:04236// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
237// and any negative value exceeds the lower boundary.
[email protected]4efb2c32014-01-16 06:57:25238template <typename Dst, typename Src>
[email protected]5bfecbc52014-02-27 13:49:04239struct DstRangeRelationToSrcRangeImpl<Dst,
240 Src,
241 INTEGER_REPRESENTATION_UNSIGNED,
242 INTEGER_REPRESENTATION_SIGNED,
243 NUMERIC_RANGE_NOT_CONTAINED> {
jschuh83882392016-04-20 23:22:53244 static constexpr RangeConstraint Check(Src value) {
[email protected]5bfecbc52014-02-27 13:49:04245 return (MaxExponent<Dst>::value >= MaxExponent<Src>::value)
246 ? GetRangeConstraint(true, value >= static_cast<Src>(0))
247 : GetRangeConstraint(
jschuhfafe0712015-09-14 20:21:24248 value <= static_cast<Src>(NarrowingRange<Dst, Src>::max()),
[email protected]5bfecbc52014-02-27 13:49:04249 value >= static_cast<Src>(0));
[email protected]4efb2c32014-01-16 06:57:25250 }
251};
252
253template <typename Dst, typename Src>
jschuh83882392016-04-20 23:22:53254inline constexpr RangeConstraint DstRangeRelationToSrcRange(Src value) {
jschuhd2d9fe02014-10-14 14:31:37255 static_assert(std::numeric_limits<Src>::is_specialized,
256 "Argument must be numeric.");
257 static_assert(std::numeric_limits<Dst>::is_specialized,
258 "Result must be numeric.");
[email protected]5bfecbc52014-02-27 13:49:04259 return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value);
[email protected]4efb2c32014-01-16 06:57:25260}
261
262} // namespace internal
263} // namespace base
264
danakj0a448602015-03-10 00:31:16265#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_