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 SAFE_MATH_IMPL_H_
6 #define SAFE_MATH_IMPL_H_
14 #include "base/compiler_specific.h"
15 #include "base/macros.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "base/template_util.h"
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.
26 // Integer promotion templates used by the portable checked integer arithmetic.
27 template <size_t Size
, bool IsSigned
>
28 struct IntegerForSizeAndSign
;
30 struct IntegerForSizeAndSign
<1, true> {
34 struct IntegerForSizeAndSign
<1, false> {
38 struct IntegerForSizeAndSign
<2, true> {
42 struct IntegerForSizeAndSign
<2, false> {
43 typedef uint16_t type
;
46 struct IntegerForSizeAndSign
<4, true> {
50 struct IntegerForSizeAndSign
<4, false> {
51 typedef uint32_t type
;
54 struct IntegerForSizeAndSign
<8, true> {
58 struct IntegerForSizeAndSign
<8, false> {
59 typedef uint64_t type
;
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).
66 template <typename Integer
>
67 struct UnsignedIntegerForSize
{
68 typedef typename enable_if
<
69 std::numeric_limits
<Integer
>::is_integer
,
70 typename IntegerForSizeAndSign
<sizeof(Integer
), false>::type
>::type type
;
73 template <typename Integer
>
74 struct SignedIntegerForSize
{
75 typedef typename enable_if
<
76 std::numeric_limits
<Integer
>::is_integer
,
77 typename IntegerForSizeAndSign
<sizeof(Integer
), true>::type
>::type type
;
80 template <typename Integer
>
81 struct TwiceWiderInteger
{
82 typedef typename enable_if
<
83 std::numeric_limits
<Integer
>::is_integer
,
84 typename IntegerForSizeAndSign
<
86 std::numeric_limits
<Integer
>::is_signed
>::type
>::type type
;
89 template <typename Integer
>
90 struct PositionOfSignBit
{
91 static const typename enable_if
<std::numeric_limits
<Integer
>::is_integer
,
92 size_t>::type value
= 8 * sizeof(Integer
) - 1;
95 // Helper templates for integer manipulations.
98 bool HasSignBit(T x
) {
99 // Cast to unsigned since right shift on signed is undefined.
100 return !!(static_cast<typename UnsignedIntegerForSize
<T
>::type
>(x
) >>
101 PositionOfSignBit
<T
>::value
);
104 // This wrapper undoes the standard integer promotions.
105 template <typename T
>
106 T
BinaryComplement(T x
) {
110 // Here are the actual portable checked integer math implementations.
111 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean
112 // way to coalesce things into the CheckedNumericState specializations below.
114 template <typename T
>
115 typename enable_if
<std::numeric_limits
<T
>::is_integer
, T
>::type
116 CheckedAdd(T x
, T y
, RangeConstraint
* validity
) {
117 // Since the value of x+y is undefined if we have a signed type, we compute
118 // it using the unsigned type of the same size.
119 typedef typename UnsignedIntegerForSize
<T
>::type UnsignedDst
;
120 UnsignedDst ux
= static_cast<UnsignedDst
>(x
);
121 UnsignedDst uy
= static_cast<UnsignedDst
>(y
);
122 UnsignedDst uresult
= ux
+ uy
;
123 // Addition is valid if the sign of (x + y) is equal to either that of x or
125 if (std::numeric_limits
<T
>::is_signed
) {
126 if (HasSignBit(BinaryComplement((uresult
^ ux
) & (uresult
^ uy
))))
127 *validity
= RANGE_VALID
;
128 else // Direction of wrap is inverse of result sign.
129 *validity
= HasSignBit(uresult
) ? RANGE_OVERFLOW
: RANGE_UNDERFLOW
;
131 } else { // Unsigned is either valid or overflow.
132 *validity
= BinaryComplement(x
) >= y
? RANGE_VALID
: RANGE_OVERFLOW
;
134 return static_cast<T
>(uresult
);
137 template <typename T
>
138 typename enable_if
<std::numeric_limits
<T
>::is_integer
, T
>::type
139 CheckedSub(T x
, T y
, RangeConstraint
* validity
) {
140 // Since the value of x+y is undefined if we have a signed type, we compute
141 // it using the unsigned type of the same size.
142 typedef typename UnsignedIntegerForSize
<T
>::type UnsignedDst
;
143 UnsignedDst ux
= static_cast<UnsignedDst
>(x
);
144 UnsignedDst uy
= static_cast<UnsignedDst
>(y
);
145 UnsignedDst uresult
= ux
- uy
;
146 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
148 if (std::numeric_limits
<T
>::is_signed
) {
149 if (HasSignBit(BinaryComplement((uresult
^ ux
) & (ux
^ uy
))))
150 *validity
= RANGE_VALID
;
151 else // Direction of wrap is inverse of result sign.
152 *validity
= HasSignBit(uresult
) ? RANGE_OVERFLOW
: RANGE_UNDERFLOW
;
154 } else { // Unsigned is either valid or underflow.
155 *validity
= x
>= y
? RANGE_VALID
: RANGE_UNDERFLOW
;
157 return static_cast<T
>(uresult
);
160 // Integer multiplication is a bit complicated. In the fast case we just
161 // we just promote to a twice wider type, and range check the result. In the
162 // slow case we need to manually check that the result won't be truncated by
163 // checking with division against the appropriate bound.
164 template <typename T
>
166 std::numeric_limits
<T
>::is_integer
&& sizeof(T
) * 2 <= sizeof(uintmax_t),
168 CheckedMul(T x
, T y
, RangeConstraint
* validity
) {
169 typedef typename TwiceWiderInteger
<T
>::type IntermediateType
;
170 IntermediateType tmp
=
171 static_cast<IntermediateType
>(x
) * static_cast<IntermediateType
>(y
);
172 *validity
= DstRangeRelationToSrcRange
<T
>(tmp
);
173 return static_cast<T
>(tmp
);
176 template <typename T
>
177 typename enable_if
<std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<
178 T
>::is_signed
&&(sizeof(T
) * 2 > sizeof(uintmax_t)),
180 CheckedMul(T x
, T y
, RangeConstraint
* validity
) {
181 // if either side is zero then the result will be zero.
188 x
<= std::numeric_limits
<T
>::max() / y
? RANGE_VALID
: RANGE_OVERFLOW
;
190 *validity
= y
>= std::numeric_limits
<T
>::min() / x
? RANGE_VALID
195 *validity
= x
>= std::numeric_limits
<T
>::min() / y
? RANGE_VALID
199 y
>= std::numeric_limits
<T
>::max() / x
? RANGE_VALID
: RANGE_OVERFLOW
;
205 template <typename T
>
206 typename enable_if
<std::numeric_limits
<T
>::is_integer
&&
207 !std::numeric_limits
<T
>::is_signed
&&
208 (sizeof(T
) * 2 > sizeof(uintmax_t)),
210 CheckedMul(T x
, T y
, RangeConstraint
* validity
) {
211 *validity
= (y
== 0 || x
<= std::numeric_limits
<T
>::max() / y
)
217 // Division just requires a check for an invalid negation on signed min/-1.
218 template <typename T
>
222 RangeConstraint
* validity
,
223 typename enable_if
<std::numeric_limits
<T
>::is_integer
, int>::type
= 0) {
224 if (std::numeric_limits
<T
>::is_signed
&& x
== std::numeric_limits
<T
>::min() &&
225 y
== static_cast<T
>(-1)) {
226 *validity
= RANGE_OVERFLOW
;
227 return std::numeric_limits
<T
>::min();
230 *validity
= RANGE_VALID
;
234 template <typename T
>
236 std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<T
>::is_signed
,
238 CheckedMod(T x
, T y
, RangeConstraint
* validity
) {
239 *validity
= y
> 0 ? RANGE_VALID
: RANGE_INVALID
;
243 template <typename T
>
245 std::numeric_limits
<T
>::is_integer
&& !std::numeric_limits
<T
>::is_signed
,
247 CheckedMod(T x
, T y
, RangeConstraint
* validity
) {
248 *validity
= RANGE_VALID
;
252 template <typename T
>
254 std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<T
>::is_signed
,
256 CheckedNeg(T value
, RangeConstraint
* validity
) {
258 value
!= std::numeric_limits
<T
>::min() ? RANGE_VALID
: RANGE_OVERFLOW
;
259 // The negation of signed min is min, so catch that one.
263 template <typename T
>
265 std::numeric_limits
<T
>::is_integer
&& !std::numeric_limits
<T
>::is_signed
,
267 CheckedNeg(T value
, RangeConstraint
* validity
) {
268 // The only legal unsigned negation is zero.
269 *validity
= value
? RANGE_UNDERFLOW
: RANGE_VALID
;
270 return static_cast<T
>(
271 -static_cast<typename SignedIntegerForSize
<T
>::type
>(value
));
274 template <typename T
>
276 std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<T
>::is_signed
,
278 CheckedAbs(T value
, RangeConstraint
* validity
) {
280 value
!= std::numeric_limits
<T
>::min() ? RANGE_VALID
: RANGE_OVERFLOW
;
281 return std::abs(value
);
284 template <typename T
>
286 std::numeric_limits
<T
>::is_integer
&& !std::numeric_limits
<T
>::is_signed
,
288 CheckedAbs(T value
, RangeConstraint
* validity
) {
289 // Absolute value of a positive is just its identiy.
290 *validity
= RANGE_VALID
;
294 // These are the floating point stubs that the compiler needs to see. Only the
295 // negation operation is ever called.
296 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
297 template <typename T> \
298 typename enable_if<std::numeric_limits<T>::is_iec559, T>::type \
299 Checked##NAME(T, T, RangeConstraint*) { \
304 BASE_FLOAT_ARITHMETIC_STUBS(Add
)
305 BASE_FLOAT_ARITHMETIC_STUBS(Sub
)
306 BASE_FLOAT_ARITHMETIC_STUBS(Mul
)
307 BASE_FLOAT_ARITHMETIC_STUBS(Div
)
308 BASE_FLOAT_ARITHMETIC_STUBS(Mod
)
310 #undef BASE_FLOAT_ARITHMETIC_STUBS
312 template <typename T
>
313 typename enable_if
<std::numeric_limits
<T
>::is_iec559
, T
>::type
CheckedNeg(
319 template <typename T
>
320 typename enable_if
<std::numeric_limits
<T
>::is_iec559
, T
>::type
CheckedAbs(
323 return std::abs(value
);
326 // Floats carry around their validity state with them, but integers do not. So,
327 // we wrap the underlying value in a specialization in order to hide that detail
328 // and expose an interface via accessors.
329 enum NumericRepresentation
{
335 template <typename NumericType
>
336 struct GetNumericRepresentation
{
337 static const NumericRepresentation value
=
338 std::numeric_limits
<NumericType
>::is_integer
340 : (std::numeric_limits
<NumericType
>::is_iec559
? NUMERIC_FLOATING
344 template <typename T
, NumericRepresentation type
=
345 GetNumericRepresentation
<T
>::value
>
346 class CheckedNumericState
{};
348 // Integrals require quite a bit of additional housekeeping to manage state.
349 template <typename T
>
350 class CheckedNumericState
<T
, NUMERIC_INTEGER
> {
353 RangeConstraint validity_
;
356 template <typename Src
, NumericRepresentation type
>
357 friend class CheckedNumericState
;
359 CheckedNumericState() : value_(0), validity_(RANGE_VALID
) {}
361 template <typename Src
>
362 CheckedNumericState(Src value
, RangeConstraint validity
)
364 validity_(GetRangeConstraint(validity
|
365 DstRangeRelationToSrcRange
<T
>(value
))) {
366 COMPILE_ASSERT(std::numeric_limits
<Src
>::is_specialized
,
367 argument_must_be_numeric
);
371 template <typename Src
>
372 CheckedNumericState(const CheckedNumericState
<Src
>& rhs
)
373 : value_(static_cast<T
>(rhs
.value())),
374 validity_(GetRangeConstraint(
375 rhs
.validity() | DstRangeRelationToSrcRange
<T
>(rhs
.value()))) {}
377 template <typename Src
>
378 explicit CheckedNumericState(
380 typename enable_if
<std::numeric_limits
<Src
>::is_specialized
, int>::type
=
382 : value_(static_cast<T
>(value
)),
383 validity_(DstRangeRelationToSrcRange
<T
>(value
)) {}
385 RangeConstraint
validity() const { return validity_
; }
386 T
value() const { return value_
; }
389 // Floating points maintain their own validity, but need translation wrappers.
390 template <typename T
>
391 class CheckedNumericState
<T
, NUMERIC_FLOATING
> {
396 template <typename Src
, NumericRepresentation type
>
397 friend class CheckedNumericState
;
399 CheckedNumericState() : value_(0.0) {}
401 template <typename Src
>
404 RangeConstraint validity
,
405 typename enable_if
<std::numeric_limits
<Src
>::is_integer
, int>::type
= 0) {
406 switch (DstRangeRelationToSrcRange
<T
>(value
)) {
408 value_
= static_cast<T
>(value
);
411 case RANGE_UNDERFLOW
:
412 value_
= -std::numeric_limits
<T
>::infinity();
416 value_
= std::numeric_limits
<T
>::infinity();
420 value_
= std::numeric_limits
<T
>::quiet_NaN();
428 template <typename Src
>
429 explicit CheckedNumericState(
431 typename enable_if
<std::numeric_limits
<Src
>::is_specialized
, int>::type
=
433 : value_(static_cast<T
>(value
)) {}
436 template <typename Src
>
437 CheckedNumericState(const CheckedNumericState
<Src
>& rhs
)
438 : value_(static_cast<T
>(rhs
.value())) {}
440 RangeConstraint
validity() const {
441 return GetRangeConstraint(value_
<= std::numeric_limits
<T
>::max(),
442 value_
>= -std::numeric_limits
<T
>::max());
444 T
value() const { return value_
; }
447 // For integers less than 128-bit and floats 32-bit or larger, we can distil
448 // C/C++ arithmetic promotions down to two simple rules:
449 // 1. The type with the larger maximum exponent always takes precedence.
450 // 2. The resulting type must be promoted to at least an int.
451 // The following template specializations implement that promotion logic.
452 enum ArithmeticPromotionCategory
{
458 template <typename Lhs
,
460 ArithmeticPromotionCategory Promotion
=
461 (MaxExponent
<Lhs
>::value
> MaxExponent
<Rhs
>::value
)
462 ? (MaxExponent
<Lhs
>::value
> MaxExponent
<int>::value
465 : (MaxExponent
<Rhs
>::value
> MaxExponent
<int>::value
467 : DEFAULT_PROMOTION
) >
468 struct ArithmeticPromotion
;
470 template <typename Lhs
, typename Rhs
>
471 struct ArithmeticPromotion
<Lhs
, Rhs
, LEFT_PROMOTION
> {
475 template <typename Lhs
, typename Rhs
>
476 struct ArithmeticPromotion
<Lhs
, Rhs
, RIGHT_PROMOTION
> {
480 template <typename Lhs
, typename Rhs
>
481 struct ArithmeticPromotion
<Lhs
, Rhs
, DEFAULT_PROMOTION
> {
485 // We can statically check if operations on the provided types can wrap, so we
486 // can skip the checked operations if they're not needed. So, for an integer we
487 // care if the destination type preserves the sign and is twice the width of
489 template <typename T
, typename Lhs
, typename Rhs
>
490 struct IsIntegerArithmeticSafe
{
491 static const bool value
= !std::numeric_limits
<T
>::is_iec559
&&
492 StaticDstRangeRelationToSrcRange
<T
, Lhs
>::value
==
493 NUMERIC_RANGE_CONTAINED
&&
494 sizeof(T
) >= (2 * sizeof(Lhs
)) &&
495 StaticDstRangeRelationToSrcRange
<T
, Rhs
>::value
!=
496 NUMERIC_RANGE_CONTAINED
&&
497 sizeof(T
) >= (2 * sizeof(Rhs
));
500 } // namespace internal
503 #endif // SAFE_MATH_IMPL_H_