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.
5 #ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_
6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_
14 #include "base/numerics/safe_conversions.h"
15 #include "base/template_util.h"
20 // Everything from here up to the floating point operations is portable C++,
21 // but it may not be fast. This code could be split based on
22 // platform/architecture and replaced with potentially faster implementations.
24 // Integer promotion templates used by the portable checked integer arithmetic.
25 template <size_t Size
, bool IsSigned
>
26 struct IntegerForSizeAndSign
;
28 struct IntegerForSizeAndSign
<1, true> {
32 struct IntegerForSizeAndSign
<1, false> {
36 struct IntegerForSizeAndSign
<2, true> {
40 struct IntegerForSizeAndSign
<2, false> {
41 typedef uint16_t type
;
44 struct IntegerForSizeAndSign
<4, true> {
48 struct IntegerForSizeAndSign
<4, false> {
49 typedef uint32_t type
;
52 struct IntegerForSizeAndSign
<8, true> {
56 struct IntegerForSizeAndSign
<8, false> {
57 typedef uint64_t type
;
60 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
61 // support 128-bit math, then the ArithmeticPromotion template below will need
62 // to be updated (or more likely replaced with a decltype expression).
64 template <typename Integer
>
65 struct UnsignedIntegerForSize
{
66 typedef typename enable_if
<
67 std::numeric_limits
<Integer
>::is_integer
,
68 typename IntegerForSizeAndSign
<sizeof(Integer
), false>::type
>::type type
;
71 template <typename Integer
>
72 struct SignedIntegerForSize
{
73 typedef typename enable_if
<
74 std::numeric_limits
<Integer
>::is_integer
,
75 typename IntegerForSizeAndSign
<sizeof(Integer
), true>::type
>::type type
;
78 template <typename Integer
>
79 struct TwiceWiderInteger
{
80 typedef typename enable_if
<
81 std::numeric_limits
<Integer
>::is_integer
,
82 typename IntegerForSizeAndSign
<
84 std::numeric_limits
<Integer
>::is_signed
>::type
>::type type
;
87 template <typename Integer
>
88 struct PositionOfSignBit
{
89 static const typename enable_if
<std::numeric_limits
<Integer
>::is_integer
,
90 size_t>::type value
= 8 * sizeof(Integer
) - 1;
93 // Helper templates for integer manipulations.
96 bool HasSignBit(T x
) {
97 // Cast to unsigned since right shift on signed is undefined.
98 return !!(static_cast<typename UnsignedIntegerForSize
<T
>::type
>(x
) >>
99 PositionOfSignBit
<T
>::value
);
102 // This wrapper undoes the standard integer promotions.
103 template <typename T
>
104 T
BinaryComplement(T x
) {
108 // Here are the actual portable checked integer math implementations.
109 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean
110 // way to coalesce things into the CheckedNumericState specializations below.
112 template <typename T
>
113 typename enable_if
<std::numeric_limits
<T
>::is_integer
, T
>::type
114 CheckedAdd(T x
, T y
, RangeConstraint
* validity
) {
115 // Since the value of x+y is undefined if we have a signed type, we compute
116 // it using the unsigned type of the same size.
117 typedef typename UnsignedIntegerForSize
<T
>::type UnsignedDst
;
118 UnsignedDst ux
= static_cast<UnsignedDst
>(x
);
119 UnsignedDst uy
= static_cast<UnsignedDst
>(y
);
120 UnsignedDst uresult
= ux
+ uy
;
121 // Addition is valid if the sign of (x + y) is equal to either that of x or
123 if (std::numeric_limits
<T
>::is_signed
) {
124 if (HasSignBit(BinaryComplement((uresult
^ ux
) & (uresult
^ uy
))))
125 *validity
= RANGE_VALID
;
126 else // Direction of wrap is inverse of result sign.
127 *validity
= HasSignBit(uresult
) ? RANGE_OVERFLOW
: RANGE_UNDERFLOW
;
129 } else { // Unsigned is either valid or overflow.
130 *validity
= BinaryComplement(x
) >= y
? RANGE_VALID
: RANGE_OVERFLOW
;
132 return static_cast<T
>(uresult
);
135 template <typename T
>
136 typename enable_if
<std::numeric_limits
<T
>::is_integer
, T
>::type
137 CheckedSub(T x
, T y
, RangeConstraint
* validity
) {
138 // Since the value of x+y is undefined if we have a signed type, we compute
139 // it using the unsigned type of the same size.
140 typedef typename UnsignedIntegerForSize
<T
>::type UnsignedDst
;
141 UnsignedDst ux
= static_cast<UnsignedDst
>(x
);
142 UnsignedDst uy
= static_cast<UnsignedDst
>(y
);
143 UnsignedDst uresult
= ux
- uy
;
144 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
146 if (std::numeric_limits
<T
>::is_signed
) {
147 if (HasSignBit(BinaryComplement((uresult
^ ux
) & (ux
^ uy
))))
148 *validity
= RANGE_VALID
;
149 else // Direction of wrap is inverse of result sign.
150 *validity
= HasSignBit(uresult
) ? RANGE_OVERFLOW
: RANGE_UNDERFLOW
;
152 } else { // Unsigned is either valid or underflow.
153 *validity
= x
>= y
? RANGE_VALID
: RANGE_UNDERFLOW
;
155 return static_cast<T
>(uresult
);
158 // Integer multiplication is a bit complicated. In the fast case we just
159 // we just promote to a twice wider type, and range check the result. In the
160 // slow case we need to manually check that the result won't be truncated by
161 // checking with division against the appropriate bound.
162 template <typename T
>
164 std::numeric_limits
<T
>::is_integer
&& sizeof(T
) * 2 <= sizeof(uintmax_t),
166 CheckedMul(T x
, T y
, RangeConstraint
* validity
) {
167 typedef typename TwiceWiderInteger
<T
>::type IntermediateType
;
168 IntermediateType tmp
=
169 static_cast<IntermediateType
>(x
) * static_cast<IntermediateType
>(y
);
170 *validity
= DstRangeRelationToSrcRange
<T
>(tmp
);
171 return static_cast<T
>(tmp
);
174 template <typename T
>
175 typename enable_if
<std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<
176 T
>::is_signed
&&(sizeof(T
) * 2 > sizeof(uintmax_t)),
178 CheckedMul(T x
, T y
, RangeConstraint
* validity
) {
179 // If either side is zero then the result will be zero.
186 x
<= std::numeric_limits
<T
>::max() / y
? RANGE_VALID
: RANGE_OVERFLOW
;
188 *validity
= y
>= std::numeric_limits
<T
>::min() / x
? RANGE_VALID
193 *validity
= x
>= std::numeric_limits
<T
>::min() / y
? RANGE_VALID
197 y
>= std::numeric_limits
<T
>::max() / x
? RANGE_VALID
: RANGE_OVERFLOW
;
203 template <typename T
>
204 typename enable_if
<std::numeric_limits
<T
>::is_integer
&&
205 !std::numeric_limits
<T
>::is_signed
&&
206 (sizeof(T
) * 2 > sizeof(uintmax_t)),
208 CheckedMul(T x
, T y
, RangeConstraint
* validity
) {
209 *validity
= (y
== 0 || x
<= std::numeric_limits
<T
>::max() / y
)
215 // Division just requires a check for an invalid negation on signed min/-1.
216 template <typename T
>
220 RangeConstraint
* validity
,
221 typename enable_if
<std::numeric_limits
<T
>::is_integer
, int>::type
= 0) {
222 if (std::numeric_limits
<T
>::is_signed
&& x
== std::numeric_limits
<T
>::min() &&
223 y
== static_cast<T
>(-1)) {
224 *validity
= RANGE_OVERFLOW
;
225 return std::numeric_limits
<T
>::min();
228 *validity
= RANGE_VALID
;
232 template <typename T
>
234 std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<T
>::is_signed
,
236 CheckedMod(T x
, T y
, RangeConstraint
* validity
) {
237 *validity
= y
> 0 ? RANGE_VALID
: RANGE_INVALID
;
241 template <typename T
>
243 std::numeric_limits
<T
>::is_integer
&& !std::numeric_limits
<T
>::is_signed
,
245 CheckedMod(T x
, T y
, RangeConstraint
* validity
) {
246 *validity
= RANGE_VALID
;
250 template <typename T
>
252 std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<T
>::is_signed
,
254 CheckedNeg(T value
, RangeConstraint
* validity
) {
256 value
!= std::numeric_limits
<T
>::min() ? RANGE_VALID
: RANGE_OVERFLOW
;
257 // The negation of signed min is min, so catch that one.
261 template <typename T
>
263 std::numeric_limits
<T
>::is_integer
&& !std::numeric_limits
<T
>::is_signed
,
265 CheckedNeg(T value
, RangeConstraint
* validity
) {
266 // The only legal unsigned negation is zero.
267 *validity
= value
? RANGE_UNDERFLOW
: RANGE_VALID
;
268 return static_cast<T
>(
269 -static_cast<typename SignedIntegerForSize
<T
>::type
>(value
));
272 template <typename T
>
274 std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<T
>::is_signed
,
276 CheckedAbs(T value
, RangeConstraint
* validity
) {
278 value
!= std::numeric_limits
<T
>::min() ? RANGE_VALID
: RANGE_OVERFLOW
;
279 return static_cast<T
>(std::abs(value
));
282 template <typename T
>
284 std::numeric_limits
<T
>::is_integer
&& !std::numeric_limits
<T
>::is_signed
,
286 CheckedAbs(T value
, RangeConstraint
* validity
) {
287 // Absolute value of a positive is just its identiy.
288 *validity
= RANGE_VALID
;
292 // These are the floating point stubs that the compiler needs to see. Only the
293 // negation operation is ever called.
294 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
295 template <typename T> \
296 typename enable_if<std::numeric_limits<T>::is_iec559, T>::type \
297 Checked##NAME(T, T, RangeConstraint*) { \
302 BASE_FLOAT_ARITHMETIC_STUBS(Add
)
303 BASE_FLOAT_ARITHMETIC_STUBS(Sub
)
304 BASE_FLOAT_ARITHMETIC_STUBS(Mul
)
305 BASE_FLOAT_ARITHMETIC_STUBS(Div
)
306 BASE_FLOAT_ARITHMETIC_STUBS(Mod
)
308 #undef BASE_FLOAT_ARITHMETIC_STUBS
310 template <typename T
>
311 typename enable_if
<std::numeric_limits
<T
>::is_iec559
, T
>::type
CheckedNeg(
317 template <typename T
>
318 typename enable_if
<std::numeric_limits
<T
>::is_iec559
, T
>::type
CheckedAbs(
321 return std::abs(value
);
324 // Floats carry around their validity state with them, but integers do not. So,
325 // we wrap the underlying value in a specialization in order to hide that detail
326 // and expose an interface via accessors.
327 enum NumericRepresentation
{
333 template <typename NumericType
>
334 struct GetNumericRepresentation
{
335 static const NumericRepresentation value
=
336 std::numeric_limits
<NumericType
>::is_integer
338 : (std::numeric_limits
<NumericType
>::is_iec559
? NUMERIC_FLOATING
342 template <typename T
, NumericRepresentation type
=
343 GetNumericRepresentation
<T
>::value
>
344 class CheckedNumericState
{};
346 // Integrals require quite a bit of additional housekeeping to manage state.
347 template <typename T
>
348 class CheckedNumericState
<T
, NUMERIC_INTEGER
> {
351 RangeConstraint validity_
;
354 template <typename Src
, NumericRepresentation type
>
355 friend class CheckedNumericState
;
357 CheckedNumericState() : value_(0), validity_(RANGE_VALID
) {}
359 template <typename Src
>
360 CheckedNumericState(Src value
, RangeConstraint validity
)
361 : value_(static_cast<T
>(value
)),
362 validity_(GetRangeConstraint(validity
|
363 DstRangeRelationToSrcRange
<T
>(value
))) {
364 static_assert(std::numeric_limits
<Src
>::is_specialized
,
365 "Argument must be numeric.");
369 template <typename Src
>
370 CheckedNumericState(const CheckedNumericState
<Src
>& rhs
)
371 : value_(static_cast<T
>(rhs
.value())),
372 validity_(GetRangeConstraint(
373 rhs
.validity() | DstRangeRelationToSrcRange
<T
>(rhs
.value()))) {}
375 template <typename Src
>
376 explicit CheckedNumericState(
378 typename enable_if
<std::numeric_limits
<Src
>::is_specialized
, int>::type
=
380 : value_(static_cast<T
>(value
)),
381 validity_(DstRangeRelationToSrcRange
<T
>(value
)) {}
383 RangeConstraint
validity() const { return validity_
; }
384 T
value() const { return value_
; }
387 // Floating points maintain their own validity, but need translation wrappers.
388 template <typename T
>
389 class CheckedNumericState
<T
, NUMERIC_FLOATING
> {
394 template <typename Src
, NumericRepresentation type
>
395 friend class CheckedNumericState
;
397 CheckedNumericState() : value_(0.0) {}
399 template <typename Src
>
402 RangeConstraint validity
,
403 typename enable_if
<std::numeric_limits
<Src
>::is_integer
, int>::type
= 0) {
404 switch (DstRangeRelationToSrcRange
<T
>(value
)) {
406 value_
= static_cast<T
>(value
);
409 case RANGE_UNDERFLOW
:
410 value_
= -std::numeric_limits
<T
>::infinity();
414 value_
= std::numeric_limits
<T
>::infinity();
418 value_
= std::numeric_limits
<T
>::quiet_NaN();
426 template <typename Src
>
427 explicit CheckedNumericState(
429 typename enable_if
<std::numeric_limits
<Src
>::is_specialized
, int>::type
=
431 : value_(static_cast<T
>(value
)) {}
434 template <typename Src
>
435 CheckedNumericState(const CheckedNumericState
<Src
>& rhs
)
436 : value_(static_cast<T
>(rhs
.value())) {}
438 RangeConstraint
validity() const {
439 return GetRangeConstraint(value_
<= std::numeric_limits
<T
>::max(),
440 value_
>= -std::numeric_limits
<T
>::max());
442 T
value() const { return value_
; }
445 // For integers less than 128-bit and floats 32-bit or larger, we can distil
446 // C/C++ arithmetic promotions down to two simple rules:
447 // 1. The type with the larger maximum exponent always takes precedence.
448 // 2. The resulting type must be promoted to at least an int.
449 // The following template specializations implement that promotion logic.
450 enum ArithmeticPromotionCategory
{
456 template <typename Lhs
,
458 ArithmeticPromotionCategory Promotion
=
459 (MaxExponent
<Lhs
>::value
> MaxExponent
<Rhs
>::value
)
460 ? (MaxExponent
<Lhs
>::value
> MaxExponent
<int>::value
463 : (MaxExponent
<Rhs
>::value
> MaxExponent
<int>::value
465 : DEFAULT_PROMOTION
) >
466 struct ArithmeticPromotion
;
468 template <typename Lhs
, typename Rhs
>
469 struct ArithmeticPromotion
<Lhs
, Rhs
, LEFT_PROMOTION
> {
473 template <typename Lhs
, typename Rhs
>
474 struct ArithmeticPromotion
<Lhs
, Rhs
, RIGHT_PROMOTION
> {
478 template <typename Lhs
, typename Rhs
>
479 struct ArithmeticPromotion
<Lhs
, Rhs
, DEFAULT_PROMOTION
> {
483 // We can statically check if operations on the provided types can wrap, so we
484 // can skip the checked operations if they're not needed. So, for an integer we
485 // care if the destination type preserves the sign and is twice the width of
487 template <typename T
, typename Lhs
, typename Rhs
>
488 struct IsIntegerArithmeticSafe
{
489 static const bool value
= !std::numeric_limits
<T
>::is_iec559
&&
490 StaticDstRangeRelationToSrcRange
<T
, Lhs
>::value
==
491 NUMERIC_RANGE_CONTAINED
&&
492 sizeof(T
) >= (2 * sizeof(Lhs
)) &&
493 StaticDstRangeRelationToSrcRange
<T
, Rhs
>::value
!=
494 NUMERIC_RANGE_CONTAINED
&&
495 sizeof(T
) >= (2 * sizeof(Rhs
));
498 } // namespace internal
501 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_