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/macros.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/template_util.h"
21 // Everything from here up to the floating point operations is portable C++,
22 // but it may not be fast. This code could be split based on
23 // platform/architecture and replaced with potentially faster implementations.
25 // Integer promotion templates used by the portable checked integer arithmetic.
26 template <size_t Size
, bool IsSigned
>
27 struct IntegerForSizeAndSign
;
29 struct IntegerForSizeAndSign
<1, true> {
33 struct IntegerForSizeAndSign
<1, false> {
37 struct IntegerForSizeAndSign
<2, true> {
41 struct IntegerForSizeAndSign
<2, false> {
42 typedef uint16_t type
;
45 struct IntegerForSizeAndSign
<4, true> {
49 struct IntegerForSizeAndSign
<4, false> {
50 typedef uint32_t type
;
53 struct IntegerForSizeAndSign
<8, true> {
57 struct IntegerForSizeAndSign
<8, false> {
58 typedef uint64_t type
;
61 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
62 // support 128-bit math, then the ArithmeticPromotion template below will need
63 // to be updated (or more likely replaced with a decltype expression).
65 template <typename Integer
>
66 struct UnsignedIntegerForSize
{
67 typedef typename enable_if
<
68 std::numeric_limits
<Integer
>::is_integer
,
69 typename IntegerForSizeAndSign
<sizeof(Integer
), false>::type
>::type type
;
72 template <typename Integer
>
73 struct SignedIntegerForSize
{
74 typedef typename enable_if
<
75 std::numeric_limits
<Integer
>::is_integer
,
76 typename IntegerForSizeAndSign
<sizeof(Integer
), true>::type
>::type type
;
79 template <typename Integer
>
80 struct TwiceWiderInteger
{
81 typedef typename enable_if
<
82 std::numeric_limits
<Integer
>::is_integer
,
83 typename IntegerForSizeAndSign
<
85 std::numeric_limits
<Integer
>::is_signed
>::type
>::type type
;
88 template <typename Integer
>
89 struct PositionOfSignBit
{
90 static const typename enable_if
<std::numeric_limits
<Integer
>::is_integer
,
91 size_t>::type value
= 8 * sizeof(Integer
) - 1;
94 // Helper templates for integer manipulations.
97 bool HasSignBit(T x
) {
98 // Cast to unsigned since right shift on signed is undefined.
99 return !!(static_cast<typename UnsignedIntegerForSize
<T
>::type
>(x
) >>
100 PositionOfSignBit
<T
>::value
);
103 // This wrapper undoes the standard integer promotions.
104 template <typename T
>
105 T
BinaryComplement(T x
) {
109 // Here are the actual portable checked integer math implementations.
110 // TODO(jschuh): Break this code out from the enable_if pattern and find a clean
111 // way to coalesce things into the CheckedNumericState specializations below.
113 template <typename T
>
114 typename enable_if
<std::numeric_limits
<T
>::is_integer
, T
>::type
115 CheckedAdd(T x
, T y
, RangeConstraint
* validity
) {
116 // Since the value of x+y is undefined if we have a signed type, we compute
117 // it using the unsigned type of the same size.
118 typedef typename UnsignedIntegerForSize
<T
>::type UnsignedDst
;
119 UnsignedDst ux
= static_cast<UnsignedDst
>(x
);
120 UnsignedDst uy
= static_cast<UnsignedDst
>(y
);
121 UnsignedDst uresult
= ux
+ uy
;
122 // Addition is valid if the sign of (x + y) is equal to either that of x or
124 if (std::numeric_limits
<T
>::is_signed
) {
125 if (HasSignBit(BinaryComplement((uresult
^ ux
) & (uresult
^ uy
))))
126 *validity
= RANGE_VALID
;
127 else // Direction of wrap is inverse of result sign.
128 *validity
= HasSignBit(uresult
) ? RANGE_OVERFLOW
: RANGE_UNDERFLOW
;
130 } else { // Unsigned is either valid or overflow.
131 *validity
= BinaryComplement(x
) >= y
? RANGE_VALID
: RANGE_OVERFLOW
;
133 return static_cast<T
>(uresult
);
136 template <typename T
>
137 typename enable_if
<std::numeric_limits
<T
>::is_integer
, T
>::type
138 CheckedSub(T x
, T y
, RangeConstraint
* validity
) {
139 // Since the value of x+y is undefined if we have a signed type, we compute
140 // it using the unsigned type of the same size.
141 typedef typename UnsignedIntegerForSize
<T
>::type UnsignedDst
;
142 UnsignedDst ux
= static_cast<UnsignedDst
>(x
);
143 UnsignedDst uy
= static_cast<UnsignedDst
>(y
);
144 UnsignedDst uresult
= ux
- uy
;
145 // Subtraction is valid if either x and y have same sign, or (x-y) and x have
147 if (std::numeric_limits
<T
>::is_signed
) {
148 if (HasSignBit(BinaryComplement((uresult
^ ux
) & (ux
^ uy
))))
149 *validity
= RANGE_VALID
;
150 else // Direction of wrap is inverse of result sign.
151 *validity
= HasSignBit(uresult
) ? RANGE_OVERFLOW
: RANGE_UNDERFLOW
;
153 } else { // Unsigned is either valid or underflow.
154 *validity
= x
>= y
? RANGE_VALID
: RANGE_UNDERFLOW
;
156 return static_cast<T
>(uresult
);
159 // Integer multiplication is a bit complicated. In the fast case we just
160 // we just promote to a twice wider type, and range check the result. In the
161 // slow case we need to manually check that the result won't be truncated by
162 // checking with division against the appropriate bound.
163 template <typename T
>
165 std::numeric_limits
<T
>::is_integer
&& sizeof(T
) * 2 <= sizeof(uintmax_t),
167 CheckedMul(T x
, T y
, RangeConstraint
* validity
) {
168 typedef typename TwiceWiderInteger
<T
>::type IntermediateType
;
169 IntermediateType tmp
=
170 static_cast<IntermediateType
>(x
) * static_cast<IntermediateType
>(y
);
171 *validity
= DstRangeRelationToSrcRange
<T
>(tmp
);
172 return static_cast<T
>(tmp
);
175 template <typename T
>
176 typename enable_if
<std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<
177 T
>::is_signed
&&(sizeof(T
) * 2 > sizeof(uintmax_t)),
179 CheckedMul(T x
, T y
, RangeConstraint
* validity
) {
180 // if either side is zero then the result will be zero.
187 x
<= std::numeric_limits
<T
>::max() / y
? RANGE_VALID
: RANGE_OVERFLOW
;
189 *validity
= y
>= std::numeric_limits
<T
>::min() / x
? RANGE_VALID
194 *validity
= x
>= std::numeric_limits
<T
>::min() / y
? RANGE_VALID
198 y
>= std::numeric_limits
<T
>::max() / x
? RANGE_VALID
: RANGE_OVERFLOW
;
204 template <typename T
>
205 typename enable_if
<std::numeric_limits
<T
>::is_integer
&&
206 !std::numeric_limits
<T
>::is_signed
&&
207 (sizeof(T
) * 2 > sizeof(uintmax_t)),
209 CheckedMul(T x
, T y
, RangeConstraint
* validity
) {
210 *validity
= (y
== 0 || x
<= std::numeric_limits
<T
>::max() / y
)
216 // Division just requires a check for an invalid negation on signed min/-1.
217 template <typename T
>
221 RangeConstraint
* validity
,
222 typename enable_if
<std::numeric_limits
<T
>::is_integer
, int>::type
= 0) {
223 if (std::numeric_limits
<T
>::is_signed
&& x
== std::numeric_limits
<T
>::min() &&
224 y
== static_cast<T
>(-1)) {
225 *validity
= RANGE_OVERFLOW
;
226 return std::numeric_limits
<T
>::min();
229 *validity
= RANGE_VALID
;
233 template <typename T
>
235 std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<T
>::is_signed
,
237 CheckedMod(T x
, T y
, RangeConstraint
* validity
) {
238 *validity
= y
> 0 ? RANGE_VALID
: RANGE_INVALID
;
242 template <typename T
>
244 std::numeric_limits
<T
>::is_integer
&& !std::numeric_limits
<T
>::is_signed
,
246 CheckedMod(T x
, T y
, RangeConstraint
* validity
) {
247 *validity
= RANGE_VALID
;
251 template <typename T
>
253 std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<T
>::is_signed
,
255 CheckedNeg(T value
, RangeConstraint
* validity
) {
257 value
!= std::numeric_limits
<T
>::min() ? RANGE_VALID
: RANGE_OVERFLOW
;
258 // The negation of signed min is min, so catch that one.
262 template <typename T
>
264 std::numeric_limits
<T
>::is_integer
&& !std::numeric_limits
<T
>::is_signed
,
266 CheckedNeg(T value
, RangeConstraint
* validity
) {
267 // The only legal unsigned negation is zero.
268 *validity
= value
? RANGE_UNDERFLOW
: RANGE_VALID
;
269 return static_cast<T
>(
270 -static_cast<typename SignedIntegerForSize
<T
>::type
>(value
));
273 template <typename T
>
275 std::numeric_limits
<T
>::is_integer
&& std::numeric_limits
<T
>::is_signed
,
277 CheckedAbs(T value
, RangeConstraint
* validity
) {
279 value
!= std::numeric_limits
<T
>::min() ? RANGE_VALID
: RANGE_OVERFLOW
;
280 return std::abs(value
);
283 template <typename T
>
285 std::numeric_limits
<T
>::is_integer
&& !std::numeric_limits
<T
>::is_signed
,
287 CheckedAbs(T value
, RangeConstraint
* validity
) {
288 // Absolute value of a positive is just its identiy.
289 *validity
= RANGE_VALID
;
293 // These are the floating point stubs that the compiler needs to see. Only the
294 // negation operation is ever called.
295 #define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
296 template <typename T> \
297 typename enable_if<std::numeric_limits<T>::is_iec559, T>::type \
298 Checked##NAME(T, T, RangeConstraint*) { \
303 BASE_FLOAT_ARITHMETIC_STUBS(Add
)
304 BASE_FLOAT_ARITHMETIC_STUBS(Sub
)
305 BASE_FLOAT_ARITHMETIC_STUBS(Mul
)
306 BASE_FLOAT_ARITHMETIC_STUBS(Div
)
307 BASE_FLOAT_ARITHMETIC_STUBS(Mod
)
309 #undef BASE_FLOAT_ARITHMETIC_STUBS
311 template <typename T
>
312 typename enable_if
<std::numeric_limits
<T
>::is_iec559
, T
>::type
CheckedNeg(
318 template <typename T
>
319 typename enable_if
<std::numeric_limits
<T
>::is_iec559
, T
>::type
CheckedAbs(
322 return std::abs(value
);
325 // Floats carry around their validity state with them, but integers do not. So,
326 // we wrap the underlying value in a specialization in order to hide that detail
327 // and expose an interface via accessors.
328 enum NumericRepresentation
{
334 template <typename NumericType
>
335 struct GetNumericRepresentation
{
336 static const NumericRepresentation value
=
337 std::numeric_limits
<NumericType
>::is_integer
339 : (std::numeric_limits
<NumericType
>::is_iec559
? NUMERIC_FLOATING
343 template <typename T
, NumericRepresentation type
=
344 GetNumericRepresentation
<T
>::value
>
345 class CheckedNumericState
{};
347 // Integrals require quite a bit of additional housekeeping to manage state.
348 template <typename T
>
349 class CheckedNumericState
<T
, NUMERIC_INTEGER
> {
352 RangeConstraint validity_
;
355 template <typename Src
, NumericRepresentation type
>
356 friend class CheckedNumericState
;
358 CheckedNumericState() : value_(0), validity_(RANGE_VALID
) {}
360 template <typename Src
>
361 CheckedNumericState(Src value
, RangeConstraint validity
)
363 validity_(GetRangeConstraint(validity
|
364 DstRangeRelationToSrcRange
<T
>(value
))) {
365 COMPILE_ASSERT(std::numeric_limits
<Src
>::is_specialized
,
366 argument_must_be_numeric
);
370 template <typename Src
>
371 CheckedNumericState(const CheckedNumericState
<Src
>& rhs
)
372 : value_(static_cast<T
>(rhs
.value())),
373 validity_(GetRangeConstraint(
374 rhs
.validity() | DstRangeRelationToSrcRange
<T
>(rhs
.value()))) {}
376 template <typename Src
>
377 explicit CheckedNumericState(
379 typename enable_if
<std::numeric_limits
<Src
>::is_specialized
, int>::type
=
381 : value_(static_cast<T
>(value
)),
382 validity_(DstRangeRelationToSrcRange
<T
>(value
)) {}
384 RangeConstraint
validity() const { return validity_
; }
385 T
value() const { return value_
; }
388 // Floating points maintain their own validity, but need translation wrappers.
389 template <typename T
>
390 class CheckedNumericState
<T
, NUMERIC_FLOATING
> {
395 template <typename Src
, NumericRepresentation type
>
396 friend class CheckedNumericState
;
398 CheckedNumericState() : value_(0.0) {}
400 template <typename Src
>
403 RangeConstraint validity
,
404 typename enable_if
<std::numeric_limits
<Src
>::is_integer
, int>::type
= 0) {
405 switch (DstRangeRelationToSrcRange
<T
>(value
)) {
407 value_
= static_cast<T
>(value
);
410 case RANGE_UNDERFLOW
:
411 value_
= -std::numeric_limits
<T
>::infinity();
415 value_
= std::numeric_limits
<T
>::infinity();
419 value_
= std::numeric_limits
<T
>::quiet_NaN();
427 template <typename Src
>
428 explicit CheckedNumericState(
430 typename enable_if
<std::numeric_limits
<Src
>::is_specialized
, int>::type
=
432 : value_(static_cast<T
>(value
)) {}
435 template <typename Src
>
436 CheckedNumericState(const CheckedNumericState
<Src
>& rhs
)
437 : value_(static_cast<T
>(rhs
.value())) {}
439 RangeConstraint
validity() const {
440 return GetRangeConstraint(value_
<= std::numeric_limits
<T
>::max(),
441 value_
>= -std::numeric_limits
<T
>::max());
443 T
value() const { return value_
; }
446 // For integers less than 128-bit and floats 32-bit or larger, we can distil
447 // C/C++ arithmetic promotions down to two simple rules:
448 // 1. The type with the larger maximum exponent always takes precedence.
449 // 2. The resulting type must be promoted to at least an int.
450 // The following template specializations implement that promotion logic.
451 enum ArithmeticPromotionCategory
{
457 template <typename Lhs
,
459 ArithmeticPromotionCategory Promotion
=
460 (MaxExponent
<Lhs
>::value
> MaxExponent
<Rhs
>::value
)
461 ? (MaxExponent
<Lhs
>::value
> MaxExponent
<int>::value
464 : (MaxExponent
<Rhs
>::value
> MaxExponent
<int>::value
466 : DEFAULT_PROMOTION
) >
467 struct ArithmeticPromotion
;
469 template <typename Lhs
, typename Rhs
>
470 struct ArithmeticPromotion
<Lhs
, Rhs
, LEFT_PROMOTION
> {
474 template <typename Lhs
, typename Rhs
>
475 struct ArithmeticPromotion
<Lhs
, Rhs
, RIGHT_PROMOTION
> {
479 template <typename Lhs
, typename Rhs
>
480 struct ArithmeticPromotion
<Lhs
, Rhs
, DEFAULT_PROMOTION
> {
484 // We can statically check if operations on the provided types can wrap, so we
485 // can skip the checked operations if they're not needed. So, for an integer we
486 // care if the destination type preserves the sign and is twice the width of
488 template <typename T
, typename Lhs
, typename Rhs
>
489 struct IsIntegerArithmeticSafe
{
490 static const bool value
= !std::numeric_limits
<T
>::is_iec559
&&
491 StaticDstRangeRelationToSrcRange
<T
, Lhs
>::value
==
492 NUMERIC_RANGE_CONTAINED
&&
493 sizeof(T
) >= (2 * sizeof(Lhs
)) &&
494 StaticDstRangeRelationToSrcRange
<T
, Rhs
>::value
!=
495 NUMERIC_RANGE_CONTAINED
&&
496 sizeof(T
) >= (2 * sizeof(Rhs
));
499 } // namespace internal
502 #endif // SAFE_MATH_IMPL_H_