Add ClampedNumeric templates
Add template classes for clamped (not sticky) saturating math.
BUG=672489
Review-Url: https://codereview.chromium.org/2945433003
Cr-Commit-Position: refs/heads/master@{#484005}
CrOS-Libchrome-Original-Commit: b180f39aa6a2edc68a5c0e2308aabe639ec029f8
diff --git a/base/numerics/README.md b/base/numerics/README.md
new file mode 100644
index 0000000..d89df9e
--- /dev/null
+++ b/base/numerics/README.md
@@ -0,0 +1,260 @@
+# `base/numerics`
+
+This directory contains templates providing well-defined semantics for safely
+handling a variety of numeric operations, including most common arithmetic
+operations and conversions.
+
+The public API is broken out into the following header files:
+
+* `checked_math.h` contains the `CheckedNumeric` template class and helper
+ functions for performing arithmetic and conversion operations that detect
+ errors and boundary conditions (e.g. overflow, truncation, etc.).
+* `clamped_math.h` contains the `ClampedNumeric` template class and
+ helper functions for performing fast, clamped (i.e. non-sticky saturating)
+ arithmetic operations and conversions.
+* `safe_conversions.h` contains the `StrictNumeric` template class and
+ a collection of custom casting templates and helper functions for safely
+ converting between a range of numeric types.
+* `safe_math.h` includes all of the previously mentioned headers.
+
+*** aside
+**Note:** The `Numeric` template types implicitly convert from C numeric types
+and `Numeric` templates that are convertable to an underlying C numeric type.
+The conversion priority for `Numeric` type coercions is:
+
+* `StrictNumeric` coerces to `ClampedNumeric` and `CheckedNumeric`
+* `ClampedNumeric` coerces to `CheckedNumeric`
+***
+
+[TOC]
+
+## Conversion functions and `StrictNumeric<>` in `safe_conversions.h`
+
+This header includes a collection of helper `constexpr` templates for safely
+performing a range of conversions, assignments, and tests.
+
+### Safe casting templates
+
+* `as_signed()` - Returns the supplied integral value as a signed type of
+ the same width.
+* `as_unsigned()` - Returns the supplied integral value as an unsigned type
+ of the same width.
+* `checked_cast<>()` - Analogous to `static_cast<>` for numeric types, except
+ that by default it will trigger a crash on an out-of-bounds conversion (e.g.
+ overflow, underflow, NaN to integral) or a compile error if the conversion
+ error can be detected at compile time. The crash handler can be overridden
+ to perform a behavior other than crashing.
+* `saturated_cast<>()` - Analogous to `static_cast` for numeric types, except
+ that it returns a saturated result when the specified numeric conversion
+ would otherwise overflow or underflow. An NaN source returns 0 by
+ default, but can be overridden to return a different result.
+* `strict_cast<>()` - Analogous to `static_cast` for numeric types, except
+ this causes a compile failure if the destination type is not large
+ enough to contain any value in the source type. It performs no runtime
+ checking and thus introduces no runtime overhead.
+
+### Other helper and conversion functions
+
+* `IsValueInRangeForNumericType<>()` - A convenience function that returns
+ true if the type supplied to the template parameter can represent the value
+ passed as an argument to the function.
+* `IsValueNegative()` - A convenience function that will accept any
+ arithmetic type as an argument and will return whether the value is less
+ than zero. Unsigned types always return false.
+* `SafeUnsignedAbs()` - Returns the absolute value of the supplied integer
+ parameter as an unsigned result (thus avoiding an overflow if the value
+ is the signed, two's complement minimum).
+
+### `StrictNumeric<>`
+
+`StrictNumeric<>` is a wrapper type that performs assignments and copies via
+the `strict_cast` template, and can perform valid arithmetic comparisons
+across any range of arithmetic types. `StrictNumeric` is the return type for
+values extracted from a `CheckedNumeric` class instance. The raw numeric value
+is extracted via `static_cast` to the underlying type or any type with
+sufficient range to represent the underlying type.
+
+* `MakeStrictNum()` - Creates a new `StrictNumeric` from the underlying type
+ of the supplied arithmetic or StrictNumeric type.
+* `SizeT` - Alias for `StrictNumeric<size_t>`.
+
+## `CheckedNumeric<>` in `checked_math.h`
+
+`CheckedNumeric<>` implements all the logic and operators for detecting integer
+boundary conditions such as overflow, underflow, and invalid conversions.
+The `CheckedNumeric` type implicitly converts from floating point and integer
+data types, and contains overloads for basic arithmetic operations (i.e.: `+`,
+`-`, `*`, `/` for all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers).
+Type promotions are a slightly modified version of the [standard C/C++ numeric
+promotions
+](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)
+with the two differences being that there is no default promotion to int
+and bitwise logical operations always return an unsigned of the wider type.
+
+### Members
+
+The unary negation, increment, and decrement operators are supported, along
+with the following unary arithmetic methods, which return a new
+`CheckedNumeric` as a result of the operation:
+
+* `Abs()` - Absolute value.
+* `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type
+ (valid for only integral types).
+* `Max()` - Returns whichever is greater of the current instance or argument.
+ The underlying return type is whichever has the greatest magnitude.
+* `Min()` - Returns whichever is lowest of the current instance or argument.
+ The underlying return type is whichever has can represent the lowest
+ number in the smallest width (e.g. int8_t over unsigned, int over
+ int8_t, and float over int).
+
+The following are for converting `CheckedNumeric` instances:
+
+* `type` - The underlying numeric type.
+* `AssignIfValid()` - Assigns the underlying value to the supplied
+ destination pointer if the value is currently valid and within the
+ range supported by the destination type. Returns true on success.
+* `Cast<>()` - Instance method returning a `CheckedNumeric` derived from
+ casting the current instance to a `CheckedNumeric` of the supplied
+ destination type.
+
+*** aside
+The following member functions return a `StrictNumeric`, which is valid for
+comparison and assignment operations, but will trigger a compile failure on
+attempts to assign to a type of insufficient range. The underlying value can
+be extracted by an explicit `static_cast` to the underlying type or any type
+with sufficient range to represent the underlying type.
+***
+
+* `IsValid()` - Returns true if the underlying numeric value is valid (i.e.
+ has not wrapped or saturated and is not the result of an invalid
+ conversion).
+* `ValueOrDie()` - Returns the underlying value. If the state is not valid
+ this call will trigger a crash by default (but may be overridden by
+ supplying an alternate handler to the template).
+* `ValueOrDefault()` - Returns the current value, or the supplied default if
+ the state is not valid (but will not crash).
+
+**Comparison operators are explicitly not provided** for `CheckedNumeric`
+types because they could result in a crash if the type is not in a valid state.
+Patterns like the following should be used instead:
+
+```cpp
+CheckedNumeric<size_t> checked_size = untrusted_input_value;
+checked_size += HEADER LENGTH;
+if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) {
+ \\ Do stuff on success...
+} else {
+ \\ Handle an error...
+}
+```
+
+### Non-member helper functions
+
+The following variadic convenience functions, which accept standard arithmetic
+or `CheckedNumeric` types, perform arithmetic operations, and return a
+`CheckedNumeric` result. The supported functions are:
+
+* `CheckAdd()` - Addition.
+* `CheckSub()` - Subtraction.
+* `CheckMul()` - Multiplication.
+* `CheckDiv()` - Division.
+* `CheckMod()` - Modulus (integer only).
+* `CheckLsh()` - Left integer shift (integer only).
+* `CheckRsh()` - Right integer shift (integer only).
+* `CheckAnd()` - Bitwise AND (integer only with unsigned result).
+* `CheckOr()` - Bitwise OR (integer only with unsigned result).
+* `CheckXor()` - Bitwise XOR (integer only with unsigned result).
+* `CheckMax()` - Maximum of supplied arguments.
+* `CheckMin()` - Minimum of supplied arguments.
+
+The following wrapper functions can be used to avoid the template
+disambiguator syntax when converting a destination type.
+
+* `IsValidForType<>()` in place of: `a.template IsValid<>()`
+* `ValueOrDieForType<>()` in place of: `a.template ValueOrDie<>()`
+* `ValueOrDefaultForType<>()` in place of: `a.template ValueOrDefault<>()`
+
+The following general utility methods is are useful for converting from
+arithmetic types to `CheckedNumeric` types:
+
+* `MakeCheckedNum()` - Creates a new `CheckedNumeric` from the underlying type
+ of the supplied arithmetic or directly convertible type.
+
+## `ClampedNumeric<>` in `clamped_math.h`
+
+`ClampedNumeric<>` implements all the logic and operators for clamped
+(non-sticky saturating) arithmetic operations and conversions. The
+`ClampedNumeric` type implicitly converts back and forth between floating point
+and integer data types, saturating on assignment as appropriate. It contains
+overloads for basic arithmetic operations (i.e.: `+`, `-`, `*`, `/` for
+all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers) along with comparison
+operators for arithmetic types of any size. Type promotions are a slightly
+modified version of the [standard C/C++ numeric promotions
+](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)
+with the two differences being that there is no default promotion to int and
+bitwise logical operations always return an unsigned of the wider type.
+
+*** aside
+Most arithmetic operations saturate normally, to the numeric limit in the
+direction of the sign. The potentially unusual cases are:
+
+* **Division:** Division by zero returns the saturated limit in the direction
+ of sign of the dividend (first argument). The one exception is 0/0, which
+ returns zero (although logically is NaN).
+* **Modulus:** Division by zero returns the dividend (first argument).
+* **Left shift:** Non-zero values saturate in the direction of the signed
+ limit (max/min), even for shifts larger than the bit width. 0 shifted any
+ amount results in 0.
+* **Right shift:** Negative values saturate to -1. Positive or 0 saturates
+ to 0.
+* **Bitwise operations:** No saturation; bit pattern is identical to
+ non-saturated bitwise operations.
+***
+
+### Members
+
+The unary negation, increment, and decrement operators are supported, along
+with the following unary arithmetic methods, which return a new
+`ClampedNumeric` as a result of the operation:
+
+* `Abs()` - Absolute value.
+* `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type
+ (valid for only integral types).
+* `Max()` - Returns whichever is greater of the current instance or argument.
+ The underlying return type is whichever has the greatest magnitude.
+* `Min()` - Returns whichever is lowest of the current instance or argument.
+ The underlying return type is whichever has can represent the lowest
+ number in the smallest width (e.g. int8_t over unsigned, int over
+ int8_t, and float over int).
+
+The following are for converting `ClampedNumeric` instances:
+
+* `type` - The underlying numeric type.
+* `Cast<>()` - Instance method returning a `ClampedNumeric` derived from
+ casting the current instance to a `ClampedNumeric` of the supplied
+ destination type.
+
+### Non-member helper functions
+
+The following variadic convenience functions, which accept standard arithmetic
+or `ClampedNumeric` types, perform arithmetic operations, and return a
+`ClampedNumeric` result. The supported functions are:
+
+* `ClampAdd()` - Addition.
+* `ClampSub()` - Subtraction.
+* `ClampMul()` - Multiplication.
+* `ClampDiv()` - Division.
+* `ClampMod()` - Modulus (integer only).
+* `ClampLsh()` - Left integer shift (integer only).
+* `ClampRsh()` - Right integer shift (integer only).
+* `ClampAnd()` - Bitwise AND (integer only with unsigned result).
+* `ClampOr()` - Bitwise OR (integer only with unsigned result).
+* `ClampXor()` - Bitwise XOR (integer only with unsigned result).
+* `ClampMax()` - Maximum of supplied arguments.
+* `ClampMin()` - Minimum of supplied arguments.
+
+The following is a general utility method that is useful for converting
+to a `ClampedNumeric` type:
+
+* `MakeClampedNum()` - Creates a new `ClampedNumeric` from the underlying type
+ of the supplied arithmetic or directly convertible type.
diff --git a/base/numerics/checked_math.h b/base/numerics/checked_math.h
index 00a88e3..1f8ea9d 100644
--- a/base/numerics/checked_math.h
+++ b/base/numerics/checked_math.h
@@ -15,82 +15,6 @@
namespace base {
namespace internal {
-// CheckedNumeric<> implements all the logic and operators for detecting integer
-// boundary conditions such as overflow, underflow, and invalid conversions.
-// The CheckedNumeric type implicitly converts from floating point and integer
-// data types, and contains overloads for basic arithmetic operations (i.e.: +,
-// -, *, / for all types and %, <<, >>, &, |, ^ for integers). Type promotions
-// are a slightly modified version of the standard C arithmetic rules with the
-// two differences being that there is no default promotion to int and bitwise
-// logical operations always return an unsigned of the wider type.
-//
-// You may also use one of the variadic convenience functions, which accept
-// standard arithmetic or CheckedNumeric types, perform arithmetic operations,
-// and return a CheckedNumeric result. The supported functions are:
-// CheckAdd() - Addition.
-// CheckSub() - Subtraction.
-// CheckMul() - Multiplication.
-// CheckDiv() - Division.
-// CheckMod() - Modulous (integer only).
-// CheckLsh() - Left integer shift (integer only).
-// CheckRsh() - Right integer shift (integer only).
-// CheckAnd() - Bitwise AND (integer only with unsigned result).
-// CheckOr() - Bitwise OR (integer only with unsigned result).
-// CheckXor() - Bitwise XOR (integer only with unsigned result).
-// CheckMax() - Maximum of supplied arguments.
-// CheckMin() - Minimum of supplied arguments.
-//
-// The unary negation, increment, and decrement operators are supported, along
-// with the following unary arithmetic methods, which return a new
-// CheckedNumeric as a result of the operation:
-// Abs() - Absolute value.
-// UnsignedAbs() - Absolute value as an equal-width unsigned underlying type
-// (valid for only integral types).
-// Max() - Returns whichever is greater of the current instance or argument.
-// The underlying return type is whichever has the greatest magnitude.
-// Min() - Returns whichever is lowest of the current instance or argument.
-// The underlying return type is whichever has can represent the lowest
-// number in the smallest width (e.g. int8_t over unsigned, int over
-// int8_t, and float over int).
-//
-// The following methods convert from CheckedNumeric to standard numeric values:
-// AssignIfValid() - Assigns the underlying value to the supplied destination
-// pointer if the value is currently valid and within the range
-// supported by the destination type. Returns true on success.
-// ****************************************************************************
-// * WARNING: All of the following functions return a StrictNumeric, which *
-// * is valid for comparison and assignment operations, but will trigger a *
-// * compile failure on attempts to assign to a type of insufficient range. *
-// ****************************************************************************
-// IsValid() - Returns true if the underlying numeric value is valid (i.e. has
-// has not wrapped and is not the result of an invalid conversion).
-// ValueOrDie() - Returns the underlying value. If the state is not valid this
-// call will crash on a CHECK.
-// ValueOrDefault() - Returns the current value, or the supplied default if the
-// state is not valid (will not trigger a CHECK).
-//
-// The following wrapper functions can be used to avoid the template
-// disambiguator syntax when converting a destination type.
-// IsValidForType<>() in place of: a.template IsValid<Dst>()
-// ValueOrDieForType<>() in place of: a.template ValueOrDie()
-// ValueOrDefaultForType<>() in place of: a.template ValueOrDefault(default)
-//
-// The following are general utility methods that are useful for converting
-// between arithmetic types and CheckedNumeric types:
-// CheckedNumeric::Cast<Dst>() - Instance method returning a CheckedNumeric
-// derived from casting the current instance to a CheckedNumeric of
-// the supplied destination type.
-// MakeCheckedNum() - Creates a new CheckedNumeric from the underlying type of
-// the supplied arithmetic, CheckedNumeric, or StrictNumeric type.
-//
-// Comparison operations are explicitly not supported because they could result
-// in a crash on an unexpected CHECK condition. You should use patterns like the
-// following for comparisons:
-// CheckedNumeric<size_t> checked_size = untrusted_input_value;
-// checked_size += HEADER LENGTH;
-// if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size)
-// Do stuff...
-
template <typename T>
class CheckedNumeric {
static_assert(std::is_arithmetic<T>::value,
@@ -377,8 +301,8 @@
template <template <typename, typename, typename> class M,
typename L,
typename R>
-CheckedNumeric<typename MathWrapper<M, L, R>::type> ChkMathOp(const L lhs,
- const R rhs) {
+CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(const L lhs,
+ const R rhs) {
using Math = typename MathWrapper<M, L, R>::math;
return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
rhs);
@@ -390,10 +314,10 @@
typename R,
typename... Args>
CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
-ChkMathOp(const L lhs, const R rhs, const Args... args) {
- auto tmp = ChkMathOp<M>(lhs, rhs);
- return tmp.IsValid() ? ChkMathOp<M>(tmp, args...)
- : decltype(ChkMathOp<M>(tmp, args...))(tmp);
+CheckMathOp(const L lhs, const R rhs, const Args... args) {
+ auto tmp = CheckMathOp<M>(lhs, rhs);
+ return tmp.IsValid() ? CheckMathOp<M>(tmp, args...)
+ : decltype(CheckMathOp<M>(tmp, args...))(tmp);
}
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)
diff --git a/base/numerics/checked_math_impl.h b/base/numerics/checked_math_impl.h
index 0492d1d..bf3a307 100644
--- a/base/numerics/checked_math_impl.h
+++ b/base/numerics/checked_math_impl.h
@@ -182,17 +182,19 @@
return !__builtin_mul_overflow(x, y, result);
#endif
using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+ // Verify the destination type can hold the result (always true for 0).
+ if (!(IsValueInRangeForNumericType<Promotion>(x) &&
+ IsValueInRangeForNumericType<Promotion>(y)) &&
+ x && y) {
+ return false;
+ }
Promotion presult;
- // Fail if either operand is out of range for the promoted type.
- // TODO(jschuh): This could be made to work for a broader range of values.
- bool is_valid = IsValueInRangeForNumericType<Promotion>(x) &&
- IsValueInRangeForNumericType<Promotion>(y);
-
+ bool is_valid = true;
if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
} else {
- is_valid &= CheckedMulImpl(static_cast<Promotion>(x),
- static_cast<Promotion>(y), &presult);
+ is_valid = CheckedMulImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
}
*result = static_cast<V>(presult);
return is_valid && IsValueInRangeForNumericType<V>(presult);
@@ -242,7 +244,7 @@
template <typename T>
bool CheckedModImpl(T x, T y, T* result) {
static_assert(std::is_integral<T>::value, "Type must be integral");
- if (y > 0) {
+ if (y) {
*result = static_cast<T>(x % y);
return true;
}
@@ -283,15 +285,15 @@
using result_type = T;
template <typename V>
static bool Do(T x, U shift, V* result) {
- using ShiftType = typename std::make_unsigned<T>::type;
- static const ShiftType kBitWidth = IntegerBitsPlusSign<T>::value;
- const ShiftType real_shift = static_cast<ShiftType>(shift);
// Signed shift is not legal on negative values.
- if (!IsValueNegative(x) && real_shift < kBitWidth) {
+ if (!IsValueNegative(x) &&
+ as_unsigned(shift) < IntegerBitsPlusSign<T>::value) {
// Just use a multiplication because it's easy.
// TODO(jschuh): This could probably be made more efficient.
- if (!std::is_signed<T>::value || real_shift != kBitWidth - 1)
- return CheckedMulOp<T, T>::Do(x, static_cast<T>(1) << shift, result);
+ if (!std::is_signed<T>::value ||
+ as_unsigned(shift) < std::numeric_limits<T>::digits)
+ return CheckedMulOp<T, T>::Do(x, T(1) << shift, result);
+ *result = 0;
return !x; // Special case zero for a full width signed shift.
}
return false;
@@ -313,8 +315,7 @@
template <typename V>
static bool Do(T x, U shift, V* result) {
// Use the type conversion push negative values out of range.
- using ShiftType = typename std::make_unsigned<T>::type;
- if (static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) {
+ if (as_unsigned(shift) < IntegerBitsPlusSign<T>::value) {
T tmp = x >> shift;
*result = static_cast<V>(tmp);
return IsValueInRangeForNumericType<V>(tmp);
diff --git a/base/numerics/clamped_math.h b/base/numerics/clamped_math.h
new file mode 100644
index 0000000..d2c4f29
--- /dev/null
+++ b/base/numerics/clamped_math.h
@@ -0,0 +1,266 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_NUMERICS_CLAMPED_MATH_H_
+#define BASE_NUMERICS_CLAMPED_MATH_H_
+
+#include <stddef.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/clamped_math_impl.h"
+
+namespace base {
+namespace internal {
+
+template <typename T>
+class ClampedNumeric {
+ static_assert(std::is_arithmetic<T>::value,
+ "ClampedNumeric<T>: T must be a numeric type.");
+
+ public:
+ using type = T;
+
+ constexpr ClampedNumeric() : value_(0) {}
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs)
+ : value_(saturated_cast<T>(rhs.value_)) {}
+
+ template <typename Src>
+ friend class ClampedNumeric;
+
+ // This is not an explicit constructor because we implicitly upgrade regular
+ // numerics to ClampedNumerics to make them easier to use.
+ template <typename Src>
+ constexpr ClampedNumeric(Src value) // NOLINT(runtime/explicit)
+ : value_(saturated_cast<T>(value)) {
+ static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ }
+
+ // This is not an explicit constructor because we want a seamless conversion
+ // from StrictNumeric types.
+ template <typename Src>
+ constexpr ClampedNumeric(
+ StrictNumeric<Src> value) // NOLINT(runtime/explicit)
+ : value_(saturated_cast<T>(static_cast<Src>(value))) {}
+
+ // Returns a ClampedNumeric of the specified type, cast from the current
+ // ClampedNumeric, and saturated to the destination type.
+ template <typename Dst>
+ constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+ return *this;
+ }
+
+ // Prototypes for the supported arithmetic operator overloads.
+ template <typename Src>
+ ClampedNumeric& operator+=(const Src rhs);
+ template <typename Src>
+ ClampedNumeric& operator-=(const Src rhs);
+ template <typename Src>
+ ClampedNumeric& operator*=(const Src rhs);
+ template <typename Src>
+ ClampedNumeric& operator/=(const Src rhs);
+ template <typename Src>
+ ClampedNumeric& operator%=(const Src rhs);
+ template <typename Src>
+ ClampedNumeric& operator<<=(const Src rhs);
+ template <typename Src>
+ ClampedNumeric& operator>>=(const Src rhs);
+ template <typename Src>
+ ClampedNumeric& operator&=(const Src rhs);
+ template <typename Src>
+ ClampedNumeric& operator|=(const Src rhs);
+ template <typename Src>
+ ClampedNumeric& operator^=(const Src rhs);
+
+ constexpr ClampedNumeric operator-() const {
+ return ClampedNumeric<T>(
+ // The negation of two's complement int min is int min, so that's the
+ // only overflow case we have to check for.
+ std::is_signed<T>::value
+ ? ((std::is_floating_point<T>::value ||
+ NegateWrapper(value_) != std::numeric_limits<T>::lowest())
+ ? NegateWrapper(value_)
+ : std::numeric_limits<T>::max())
+ : T(0)); // Clamped unsigned negation is always zero.
+ }
+
+ constexpr ClampedNumeric operator~() const {
+ return ClampedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(value_));
+ }
+
+ constexpr ClampedNumeric Abs() const {
+ return ClampedNumeric<T>(
+ // The negation of two's complement int min is int min, so that's the
+ // only overflow case we have to check for.
+ (!std::is_signed<T>::value || std::is_floating_point<T>::value ||
+ AbsWrapper(value_) != std::numeric_limits<T>::lowest())
+ ? AbsWrapper(value_)
+ : std::numeric_limits<T>::max());
+ }
+
+ template <typename U>
+ constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max(
+ const U rhs) const {
+ using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type;
+ return ClampedNumeric<result_type>(
+ ClampedMaxOp<T, U>(value_, Wrapper<U>::value(rhs)));
+ }
+
+ template <typename U>
+ constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min(
+ const U rhs) const {
+ using result_type = typename MathWrapper<ClampedMinOp, T, U>::type;
+ return ClampedNumeric<result_type>(
+ ClampedMinOp<T, U>(value_, Wrapper<U>::value(rhs)));
+ }
+
+ // This function is available only for integral types. It returns an unsigned
+ // integer of the same width as the source type, containing the absolute value
+ // of the source, and properly handling signed min.
+ constexpr ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>
+ UnsignedAbs() const {
+ return ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>(
+ SafeUnsignedAbs(value_));
+ }
+
+ ClampedNumeric& operator++() {
+ *this += 1;
+ return *this;
+ }
+
+ ClampedNumeric operator++(int) {
+ ClampedNumeric value = *this;
+ *this += 1;
+ return value;
+ }
+
+ ClampedNumeric& operator--() {
+ *this -= 1;
+ return *this;
+ }
+
+ ClampedNumeric operator--(int) {
+ ClampedNumeric value = *this;
+ *this -= 1;
+ return value;
+ }
+
+ // These perform the actual math operations on the ClampedNumerics.
+ // Binary arithmetic operations.
+ template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+ static ClampedNumeric MathOp(const L lhs, const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ return ClampedNumeric<T>(
+ Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs)));
+ }
+
+ // Assignment arithmetic operations.
+ template <template <typename, typename, typename> class M, typename R>
+ ClampedNumeric& MathOp(const R rhs) {
+ using Math = typename MathWrapper<M, T, R>::math;
+ *this =
+ ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs)));
+ return *this;
+ }
+
+ template <typename Dst>
+ constexpr operator Dst() const {
+ return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(
+ value_);
+ }
+
+ private:
+ T value_;
+
+ // These wrappers allow us to handle state the same way for both
+ // ClampedNumeric and POD arithmetic types.
+ template <typename Src>
+ struct Wrapper {
+ static constexpr Src value(Src value) {
+ return static_cast<typename UnderlyingType<Src>::type>(value);
+ }
+ };
+};
+
+// Convience wrapper to return a new ClampedNumeric from the provided arithmetic
+// or ClampedNumericType.
+template <typename T>
+constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
+ const T value) {
+ return value;
+}
+
+// Overload the ostream output operator to make logging work nicely.
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {
+ os << static_cast<T>(value);
+ return os;
+}
+
+// These implement the variadic wrapper for the math operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp(const L lhs,
+ const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
+ rhs);
+}
+
+// General purpose wrapper template for arithmetic operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+ClampedNumeric<typename ResultType<M, L, R, Args...>::type>
+ClampMathOp(const L lhs, const R rhs, const Args... args) {
+ return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...);
+}
+
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Add, +, +=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Sub, -, -=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mul, *, *=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Div, /, /=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mod, %, %=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Lsh, <<, <<=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Rsh, >>, >>=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, And, &, &=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=);
+
+} // namespace internal
+
+using internal::ClampedNumeric;
+using internal::MakeClampedNum;
+using internal::ClampMax;
+using internal::ClampMin;
+using internal::ClampAdd;
+using internal::ClampSub;
+using internal::ClampMul;
+using internal::ClampDiv;
+using internal::ClampMod;
+using internal::ClampLsh;
+using internal::ClampRsh;
+using internal::ClampAnd;
+using internal::ClampOr;
+using internal::ClampXor;
+
+} // namespace base
+
+#endif // BASE_NUMERICS_CLAMPED_MATH_H_
diff --git a/base/numerics/clamped_math_impl.h b/base/numerics/clamped_math_impl.h
new file mode 100644
index 0000000..7f8035c
--- /dev/null
+++ b/base/numerics/clamped_math_impl.h
@@ -0,0 +1,290 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
+#define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/checked_math.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/numerics/safe_math_shared_impl.h"
+
+namespace base {
+namespace internal {
+
+// This provides a small optimization that generates more compact code when one
+// of the components in an operation is a compile-time constant.
+template <typename T>
+constexpr bool IsCompileTimeConstant(const T v) {
+#if defined(__clang__) || defined(__GNUC__)
+ return __builtin_constant_p(v);
+#else
+ return false;
+#endif
+}
+
+// This is a wrapper to generate return the max or min for a supplied type.
+// If the argument is false, the returned value is the maximum. If true the
+// returned value is the minimum.
+template <typename T>
+constexpr T GetMaxOrMin(bool is_min) {
+ // For both signed and unsigned math the bit pattern for minimum is really
+ // just one plus the maximum. However, we have to cast to unsigned to ensure
+ // we get well-defined overflow semantics.
+ return as_unsigned(std::numeric_limits<T>::max()) + is_min;
+}
+
+template <typename T, typename U, class Enable = void>
+struct ClampedAddOp {};
+
+template <typename T, typename U>
+struct ClampedAddOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static V Do(T x, U y) {
+ V result;
+ return CheckedAddOp<T, U>::Do(x, y, &result)
+ ? result
+ // Prefer a compile-time constant (if we have one).
+ : GetMaxOrMin<V>(IsCompileTimeConstant(x) ? IsValueNegative(x)
+ : IsValueNegative(y));
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedSubOp {};
+
+template <typename T, typename U>
+struct ClampedSubOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static V Do(T x, U y) {
+ V result;
+ return CheckedSubOp<T, U>::Do(x, y, &result)
+ ? result
+ // Prefer a compile-time constant (if we have one).
+ : GetMaxOrMin<V>(IsCompileTimeConstant(x) ? IsValueNegative(x)
+ : !IsValueNegative(y));
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMulOp {};
+
+template <typename T, typename U>
+struct ClampedMulOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static V Do(T x, U y) {
+ V result;
+ return CheckedMulOp<T, U>::Do(x, y, &result)
+ ? result
+ : GetMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedDivOp {};
+
+template <typename T, typename U>
+struct ClampedDivOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static V Do(T x, U y) {
+ V result = SaturationDefaultLimits<V>::NaN();
+ return !x || CheckedDivOp<T, U>::Do(x, y, &result)
+ ? result
+ : GetMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedModOp {};
+
+template <typename T, typename U>
+struct ClampedModOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static V Do(T x, U y) {
+ V result;
+ return CheckedModOp<T, U>::Do(x, y, &result) ? result : x;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedLshOp {};
+
+// Left shift. Non-zero values saturate in the direction of the sign. A zero
+// shifted by any value always results in zero.
+// Note: This class template supports left shifting negative values.
+template <typename T, typename U>
+struct ClampedLshOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = T;
+ template <typename V = result_type>
+ static V Do(T x, U shift) {
+ static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
+ V result = x;
+ return (shift < std::numeric_limits<T>::digits &&
+ CheckedMulOp<T, T>::Do(x, T(1) << shift, &result))
+ ? result
+ : (x ? GetMaxOrMin<V>(IsValueNegative(x)) : 0);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedRshOp {};
+
+// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
+template <typename T, typename U>
+struct ClampedRshOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = T;
+ template <typename V = result_type>
+ static V Do(T x, U shift) {
+ static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
+ return shift < IntegerBitsPlusSign<T>::value
+ ? saturated_cast<V>(x >> shift)
+ // Signed right shift is odd, because it saturates to -1 or 0.
+ : as_unsigned(V(0)) - IsValueNegative(x);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedAndOp {};
+
+template <typename T, typename U>
+struct ClampedAndOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr V Do(T x, U y) {
+ return static_cast<result_type>(x) & static_cast<result_type>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedOrOp {};
+
+// For simplicity we promote to unsigned integers.
+template <typename T, typename U>
+struct ClampedOrOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr V Do(T x, U y) {
+ return static_cast<result_type>(x) | static_cast<result_type>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedXorOp {};
+
+// For simplicity we support only unsigned integers.
+template <typename T, typename U>
+struct ClampedXorOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr V Do(T x, U y) {
+ return static_cast<result_type>(x) ^ static_cast<result_type>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMaxOp {};
+
+template <typename T, typename U>
+struct ClampedMaxOp<
+ T,
+ U,
+ typename std::enable_if<std::is_arithmetic<T>::value &&
+ std::is_arithmetic<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
+ : saturated_cast<V>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMinOp {};
+
+template <typename T, typename U>
+struct ClampedMinOp<
+ T,
+ U,
+ typename std::enable_if<std::is_arithmetic<T>::value &&
+ std::is_arithmetic<U>::value>::type> {
+ using result_type = typename LowestValuePromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
+ : saturated_cast<V>(y);
+ }
+};
+
+// This is just boilerplate that wraps the standard floating point arithmetic.
+// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
+ template <typename T, typename U> \
+ struct Clamped##NAME##Op< \
+ T, U, \
+ typename std::enable_if<std::is_floating_point<T>::value || \
+ std::is_floating_point<U>::value>::type> { \
+ using result_type = typename MaxExponentPromotion<T, U>::type; \
+ template <typename V = result_type> \
+ static constexpr V Do(T x, U y) { \
+ return saturated_cast<V>(x OP y); \
+ } \
+ };
+
+BASE_FLOAT_ARITHMETIC_OPS(Add, +)
+BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
+BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
+BASE_FLOAT_ARITHMETIC_OPS(Div, /)
+
+#undef BASE_FLOAT_ARITHMETIC_OPS
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h
index b0ec279..c85e7c3 100644
--- a/base/numerics/safe_conversions.h
+++ b/base/numerics/safe_conversions.h
@@ -15,38 +15,6 @@
namespace base {
-// The following are helper constexpr template functions and classes for safely
-// performing a range of conversions, assignments, and tests:
-//
-// checked_cast<> - Analogous to static_cast<> for numeric types, except
-// that it CHECKs that the specified numeric conversion will not overflow
-// or underflow. NaN source will always trigger a CHECK.
-// The default CHECK triggers a crash, but the handler can be overriden.
-// saturated_cast<> - Analogous to static_cast<> for numeric types, except
-// that it returns a saturated result when the specified numeric conversion
-// would otherwise overflow or underflow. An NaN source returns 0 by
-// default, but can be overridden to return a different result.
-// strict_cast<> - Analogous to static_cast<> for numeric types, except that
-// it will cause a compile failure if the destination type is not large
-// enough to contain any value in the source type. It performs no runtime
-// checking and thus introduces no runtime overhead.
-// IsValueInRangeForNumericType<>() - A convenience function that returns true
-// if the type supplied to the template parameter can represent the value
-// passed as an argument to the function.
-// IsValueNegative<>() - A convenience function that will accept any arithmetic
-// type as an argument and will return whether the value is less than zero.
-// Unsigned types always return false.
-// SafeUnsignedAbs() - Returns the absolute value of the supplied integer
-// parameter as an unsigned result (thus avoiding an overflow if the value
-// is the signed, two's complement minimum).
-// StrictNumeric<> - A wrapper type that performs assignments and copies via
-// the strict_cast<> template, and can perform valid arithmetic comparisons
-// across any range of arithmetic types. StrictNumeric is the return type
-// for values extracted from a CheckedNumeric class instance. The raw
-// arithmetic value is extracted via static_cast to the underlying type.
-// MakeStrictNum() - Creates a new StrictNumeric from the underlying type of
-// the supplied arithmetic or StrictNumeric type.
-
// Convenience function that returns true if the supplied value is in range
// for the destination type.
template <typename Dst, typename Src>
@@ -82,21 +50,47 @@
: CheckHandler::template HandleFailure<Dst>();
}
+// as_signed<> returns the supplied integral value (or integral castable
+// Numeric template) cast as a signed integral of equivalent precision.
+// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
+template <typename Src>
+constexpr typename std::make_signed<
+ typename base::internal::UnderlyingType<Src>::type>::type
+as_signed(const Src value) {
+ static_assert(std::is_integral<decltype(as_signed(value))>::value,
+ "Argument must be a signed or unsigned integer type.");
+ return static_cast<decltype(as_signed(value))>(value);
+}
+
+// as_unsigned<> returns the supplied integral value (or integral castable
+// Numeric template) cast as an unsigned integral of equivalent precision.
+// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
+template <typename Src>
+constexpr typename std::make_unsigned<
+ typename base::internal::UnderlyingType<Src>::type>::type
+as_unsigned(const Src value) {
+ static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
+ "Argument must be a signed or unsigned integer type.");
+ return static_cast<decltype(as_unsigned(value))>(value);
+}
+
// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
+// You may provide your own limits (e.g. to saturated_cast) so long as you
+// implement all of the static constexpr member functions in the class below.
template <typename T>
-struct SaturationDefaultHandler {
+struct SaturationDefaultLimits : public std::numeric_limits<T> {
static constexpr T NaN() {
return std::numeric_limits<T>::has_quiet_NaN
? std::numeric_limits<T>::quiet_NaN()
: T();
}
- static constexpr T max() { return std::numeric_limits<T>::max(); }
+ using std::numeric_limits<T>::max;
static constexpr T Overflow() {
return std::numeric_limits<T>::has_infinity
? std::numeric_limits<T>::infinity()
: std::numeric_limits<T>::max();
}
- static constexpr T lowest() { return std::numeric_limits<T>::lowest(); }
+ using std::numeric_limits<T>::lowest;
static constexpr T Underflow() {
return std::numeric_limits<T>::has_infinity
? std::numeric_limits<T>::infinity() * -1
@@ -124,8 +118,7 @@
// overflow or underflow, and NaN assignment to an integral will return 0.
// All boundary condition behaviors can be overriden with a custom handler.
template <typename Dst,
- template <typename>
- class SaturationHandler = SaturationDefaultHandler,
+ template <typename> class SaturationHandler = SaturationDefaultLimits,
typename Src>
constexpr Dst saturated_cast(Src value) {
using SrcType = typename UnderlyingType<Src>::type;
@@ -238,24 +231,23 @@
return os;
}
-#define STRICT_COMPARISON_OP(NAME, OP) \
- template <typename L, typename R, \
- typename std::enable_if< \
- internal::IsStrictOp<L, R>::value>::type* = nullptr> \
- constexpr bool operator OP(const L lhs, const R rhs) { \
- return SafeCompare<NAME, typename UnderlyingType<L>::type, \
- typename UnderlyingType<R>::type>(lhs, rhs); \
+#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
+ template <typename L, typename R, \
+ typename std::enable_if< \
+ internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
+ constexpr bool operator OP(const L lhs, const R rhs) { \
+ return SafeCompare<NAME, typename UnderlyingType<L>::type, \
+ typename UnderlyingType<R>::type>(lhs, rhs); \
}
-STRICT_COMPARISON_OP(IsLess, <);
-STRICT_COMPARISON_OP(IsLessOrEqual, <=);
-STRICT_COMPARISON_OP(IsGreater, >);
-STRICT_COMPARISON_OP(IsGreaterOrEqual, >=);
-STRICT_COMPARISON_OP(IsEqual, ==);
-STRICT_COMPARISON_OP(IsNotEqual, !=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==);
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=);
-#undef STRICT_COMPARISON_OP
-};
+}; // namespace internal
using internal::strict_cast;
using internal::saturated_cast;
diff --git a/base/numerics/safe_conversions_impl.h b/base/numerics/safe_conversions_impl.h
index aef7135..713b8ef 100644
--- a/base/numerics/safe_conversions_impl.h
+++ b/base/numerics/safe_conversions_impl.h
@@ -549,6 +549,7 @@
using type = typename ArithmeticOrUnderlyingEnum<T>::type;
static const bool is_numeric = std::is_arithmetic<type>::value;
static const bool is_checked = false;
+ static const bool is_clamped = false;
static const bool is_strict = false;
};
@@ -590,14 +591,17 @@
struct IsClampedOp {
static const bool value =
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
- (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
+ (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
+ !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
};
template <typename L, typename R>
struct IsStrictOp {
static const bool value =
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
- (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict);
+ (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
+ !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
+ !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
};
template <typename L, typename R>
diff --git a/base/numerics/safe_math.h b/base/numerics/safe_math.h
index 810a13a..e30be90 100644
--- a/base/numerics/safe_math.h
+++ b/base/numerics/safe_math.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,6 +6,7 @@
#define BASE_NUMERICS_SAFE_MATH_H_
#include "base/numerics/checked_math.h"
+#include "base/numerics/clamped_math.h"
#include "base/numerics/safe_conversions.h"
#endif // BASE_NUMERICS_SAFE_MATH_H_
diff --git a/base/numerics/safe_math_shared_impl.h b/base/numerics/safe_math_shared_impl.h
index 3e75bfe..c243e26 100644
--- a/base/numerics/safe_math_shared_impl.h
+++ b/base/numerics/safe_math_shared_impl.h
@@ -118,26 +118,27 @@
template <typename L, typename R, typename... Args> \
CLASS##Numeric<typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \
CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \
- return ChkMathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, args...); \
+ return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \
+ args...); \
}
#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \
- /* Binary arithmetic operator for all CheckedNumeric operations. */ \
+ /* Binary arithmetic operator for all CLASS##Numeric operations. */ \
template <typename L, typename R, \
- typename std::enable_if<IsCheckedOp<L, R>::value>::type* = \
+ typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \
nullptr> \
- CheckedNumeric<typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \
+ CLASS##Numeric<typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \
operator OP(const L lhs, const R rhs) { \
return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \
rhs); \
} \
- /* Assignment arithmetic operator implementation from CheckedNumeric. */ \
+ /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \
template <typename L> \
template <typename R> \
- CheckedNumeric<L>& CheckedNumeric<L>::operator CMP_OP(const R rhs) { \
+ CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP(const R rhs) { \
return MathOp<CLASS##OP_NAME##Op>(rhs); \
} \
- /* Variadic arithmetic functions that return CheckedNumeric. */ \
+ /* Variadic arithmetic functions that return CLASS##Numeric. */ \
BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)
} // namespace internal
diff --git a/base/numerics/safe_numerics_unittest.cc b/base/numerics/safe_numerics_unittest.cc
index ec6d003..9fd9e8f 100644
--- a/base/numerics/safe_numerics_unittest.cc
+++ b/base/numerics/safe_numerics_unittest.cc
@@ -22,10 +22,12 @@
using std::numeric_limits;
using base::CheckedNumeric;
+using base::ClampedNumeric;
using base::IsValidForType;
using base::ValueOrDieForType;
using base::ValueOrDefaultForType;
using base::MakeCheckedNum;
+using base::MakeClampedNum;
using base::CheckMax;
using base::CheckMin;
using base::CheckAdd;
@@ -35,9 +37,20 @@
using base::CheckMod;
using base::CheckLsh;
using base::CheckRsh;
+using base::ClampMax;
+using base::ClampMin;
+using base::ClampAdd;
+using base::ClampSub;
+using base::ClampMul;
+using base::ClampDiv;
+using base::ClampMod;
+using base::ClampLsh;
+using base::ClampRsh;
+using base::as_unsigned;
using base::checked_cast;
using base::IsValueInRangeForNumericType;
using base::IsValueNegative;
+using base::SaturationDefaultLimits;
using base::SizeT;
using base::StrictNumeric;
using base::MakeStrictNum;
@@ -131,6 +144,17 @@
U GetNumericValueForTest(const CheckedNumeric<U>& src) {
return src.state_.value();
}
+
+template <typename U>
+U GetNumericValueForTest(const ClampedNumeric<U>& src) {
+ return static_cast<U>(src);
+}
+
+template <typename U>
+U GetNumericValueForTest(const U& src) {
+ return src;
+}
+
} // namespace internal.
} // namespace base.
@@ -145,6 +169,36 @@
}
};
+template <typename T>
+constexpr T GetValue(const T& src) {
+ return src;
+}
+
+template <typename T, typename U>
+constexpr T GetValueAsDest(const U& src) {
+ return static_cast<T>(src);
+}
+
+template <typename T>
+constexpr T GetValue(const CheckedNumeric<T>& src) {
+ return src.template ValueOrDie<T, LogOnFailure>();
+}
+
+template <typename T, typename U>
+constexpr T GetValueAsDest(const CheckedNumeric<U>& src) {
+ return src.template ValueOrDie<T, LogOnFailure>();
+}
+
+template <typename T>
+constexpr T GetValue(const ClampedNumeric<T>& src) {
+ return static_cast<T>(src);
+}
+
+template <typename T, typename U>
+constexpr T GetValueAsDest(const ClampedNumeric<U>& src) {
+ return static_cast<T>(src);
+}
+
// Helper macros to wrap displaying the conversion types and line numbers.
#define TEST_EXPECTED_VALIDITY(expected, actual) \
EXPECT_EQ(expected, (actual).template Cast<Dst>().IsValid()) \
@@ -156,12 +210,7 @@
// We have to handle promotions, so infer the underlying type below from actual.
#define TEST_EXPECTED_VALUE(expected, actual) \
- EXPECT_EQ(static_cast<typename std::decay<decltype(actual)>::type::type>( \
- expected), \
- ((actual) \
- .template ValueOrDie< \
- typename std::decay<decltype(actual)>::type::type, \
- LogOnFailure>())) \
+ EXPECT_EQ(GetValue(expected), GetValueAsDest<decltype(expected)>(actual)) \
<< "Result test: Value " << GetNumericValueForTest(actual) << " as " \
<< dst << " on line " << line
@@ -190,18 +239,34 @@
typename std::enable_if<numeric_limits<Dst>::is_integer &&
numeric_limits<Dst>::is_signed,
int>::type = 0) {
- using DstLimits = numeric_limits<Dst>;
+ using DstLimits = SaturationDefaultLimits<Dst>;
TEST_EXPECTED_FAILURE(-CheckedNumeric<Dst>(DstLimits::lowest()));
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
TEST_EXPECTED_VALUE(DstLimits::max(),
MakeCheckedNum(-DstLimits::max()).Abs());
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ -ClampedNumeric<Dst>(DstLimits::lowest()));
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()).Abs());
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(-1).Abs());
+ TEST_EXPECTED_VALUE(DstLimits::max(),
+ MakeClampedNum(-DstLimits::max()).Abs());
+
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) + -1);
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) +
DstLimits::lowest());
+ TEST_EXPECTED_VALUE(DstLimits::max() - 1,
+ ClampedNumeric<Dst>(DstLimits::max()) + -1);
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) + -1);
+ TEST_EXPECTED_VALUE(
+ DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) + DstLimits::lowest());
+
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) - 1);
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()) - -1);
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) -
@@ -209,7 +274,20 @@
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) -
DstLimits::max());
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) - 1);
+ TEST_EXPECTED_VALUE(DstLimits::lowest() + 1,
+ ClampedNumeric<Dst>(DstLimits::lowest()) - -1);
+ TEST_EXPECTED_VALUE(
+ DstLimits::Overflow(),
+ ClampedNumeric<Dst>(DstLimits::max()) - DstLimits::lowest());
+ TEST_EXPECTED_VALUE(
+ DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) - DstLimits::max());
+
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) * 2);
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) / -1);
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(-1) / 2);
@@ -222,19 +300,42 @@
CheckedNumeric<Dst>(DstLimits::lowest()) * Dst(1));
TEST_EXPECTED_VALUE(DstLimits::lowest(),
CheckedNumeric<Dst>(1) * Dst(DstLimits::lowest()));
- TEST_EXPECTED_VALUE(DstLimits::lowest(),
- MakeCheckedNum(DstLimits::lowest()).UnsignedAbs());
+ TEST_EXPECTED_VALUE(
+ typename std::make_unsigned<Dst>::type(0) - DstLimits::lowest(),
+ MakeCheckedNum(DstLimits::lowest()).UnsignedAbs());
TEST_EXPECTED_VALUE(DstLimits::max(),
MakeCheckedNum(DstLimits::max()).UnsignedAbs());
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0).UnsignedAbs());
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).UnsignedAbs());
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).UnsignedAbs());
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) / -1);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(-1) / 2);
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) * -1);
+ TEST_EXPECTED_VALUE(DstLimits::max(),
+ ClampedNumeric<Dst>(DstLimits::lowest() + 1) * Dst(-1));
+ TEST_EXPECTED_VALUE(DstLimits::max(),
+ ClampedNumeric<Dst>(-1) * Dst(DstLimits::lowest() + 1));
+ TEST_EXPECTED_VALUE(DstLimits::lowest(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) * Dst(1));
+ TEST_EXPECTED_VALUE(DstLimits::lowest(),
+ ClampedNumeric<Dst>(1) * Dst(DstLimits::lowest()));
+ TEST_EXPECTED_VALUE(
+ typename std::make_unsigned<Dst>::type(0) - DstLimits::lowest(),
+ MakeClampedNum(DstLimits::lowest()).UnsignedAbs());
+ TEST_EXPECTED_VALUE(DstLimits::max(),
+ MakeClampedNum(DstLimits::max()).UnsignedAbs());
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0).UnsignedAbs());
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1).UnsignedAbs());
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(-1).UnsignedAbs());
+
// Modulus is legal only for integers.
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
TEST_EXPECTED_VALUE(-1, CheckedNumeric<Dst>(-1) % 2);
- TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(-1) % -2);
+ TEST_EXPECTED_VALUE(-1, CheckedNumeric<Dst>(-1) % -2);
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::lowest()) % 2);
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
// Test all the different modulus combinations.
@@ -266,6 +367,44 @@
0, CheckedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1));
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) >> negative_one);
+ // Modulus is legal only for integers.
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>() % 1);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % 1);
+ TEST_EXPECTED_VALUE(-1, ClampedNumeric<Dst>(-1) % 2);
+ TEST_EXPECTED_VALUE(-1, ClampedNumeric<Dst>(-1) % -2);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(DstLimits::lowest()) % 2);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(DstLimits::max()) % 2);
+ // Test all the different modulus combinations.
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, 1 % ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % 1);
+ ClampedNumeric<Dst> clamped_dst = 1;
+ TEST_EXPECTED_VALUE(0, clamped_dst %= 1);
+ TEST_EXPECTED_VALUE(Dst(1), ClampedNumeric<Dst>(1) % 0);
+ // Test bit shifts.
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ ClampedNumeric<Dst>(1)
+ << (IntegerBitsPlusSign<Dst>::value - 1U));
+ TEST_EXPECTED_VALUE(Dst(0), ClampedNumeric<Dst>(0)
+ << (IntegerBitsPlusSign<Dst>::value + 0U));
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ ClampedNumeric<Dst>(DstLimits::max()) << 1U);
+ TEST_EXPECTED_VALUE(
+ static_cast<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 2U),
+ ClampedNumeric<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 2U));
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0)
+ << (IntegerBitsPlusSign<Dst>::value - 1U));
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) << 0U);
+ TEST_EXPECTED_VALUE(2, ClampedNumeric<Dst>(1) << 1U);
+ TEST_EXPECTED_VALUE(
+ 0, ClampedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value + 0U));
+ TEST_EXPECTED_VALUE(
+ 0, ClampedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1U));
+ TEST_EXPECTED_VALUE(
+ -1, ClampedNumeric<Dst>(-1) >> (IntegerBitsPlusSign<Dst>::value - 1U));
+ TEST_EXPECTED_VALUE(-1, ClampedNumeric<Dst>(DstLimits::lowest()) >>
+ (IntegerBitsPlusSign<Dst>::value - 0U));
+
TestStrictPointerMath<Dst>();
}
@@ -277,7 +416,7 @@
typename std::enable_if<numeric_limits<Dst>::is_integer &&
!numeric_limits<Dst>::is_signed,
int>::type = 0) {
- using DstLimits = numeric_limits<Dst>;
+ using DstLimits = SaturationDefaultLimits<Dst>;
TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::lowest()));
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) + -1);
@@ -296,6 +435,29 @@
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(0).UnsignedAbs());
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).UnsignedAbs());
+ TEST_EXPECTED_VALUE(0, -ClampedNumeric<Dst>(DstLimits::lowest()));
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(DstLimits::lowest()).Abs());
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) + -1);
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) - 1);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(DstLimits::lowest()) * 2);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) / 2);
+ TEST_EXPECTED_VALUE(0,
+ ClampedNumeric<Dst>(DstLimits::lowest()).UnsignedAbs());
+ TEST_EXPECTED_VALUE(
+ as_unsigned(
+ std::numeric_limits<typename std::make_signed<Dst>::type>::lowest()),
+ ClampedNumeric<typename std::make_signed<Dst>::type>(
+ std::numeric_limits<typename std::make_signed<Dst>::type>::lowest())
+ .UnsignedAbs());
+ TEST_EXPECTED_VALUE(DstLimits::lowest(),
+ MakeClampedNum(DstLimits::lowest()).UnsignedAbs());
+ TEST_EXPECTED_VALUE(DstLimits::max(),
+ MakeClampedNum(DstLimits::max()).UnsignedAbs());
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0).UnsignedAbs());
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1).UnsignedAbs());
+
// Modulus is legal only for integers.
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
@@ -350,6 +512,57 @@
CheckedNumeric<Dst>(0) ^ static_cast<Dst>(-1));
TEST_EXPECTED_VALUE(DstLimits::max(), ~CheckedNumeric<Dst>(0));
+ // Modulus is legal only for integers.
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>() % 1);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % 1);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) % 2);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(DstLimits::lowest()) % 2);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(DstLimits::max()) % 2);
+ // Test all the different modulus combinations.
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, 1 % ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) % 1);
+ ClampedNumeric<Dst> clamped_dst = 1;
+ TEST_EXPECTED_VALUE(0, clamped_dst %= 1);
+ // Test that div by 0 is avoided but returns invalid result.
+ TEST_EXPECTED_VALUE(Dst(1), ClampedNumeric<Dst>(1) % 0);
+ // Test bit shifts.
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ ClampedNumeric<Dst>(1)
+ << as_unsigned(IntegerBitsPlusSign<Dst>::value));
+ TEST_EXPECTED_VALUE(Dst(0), ClampedNumeric<Dst>(0) << as_unsigned(
+ IntegerBitsPlusSign<Dst>::value));
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ ClampedNumeric<Dst>(DstLimits::max()) << 1U);
+ TEST_EXPECTED_VALUE(
+ static_cast<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 1U),
+ ClampedNumeric<Dst>(1) << (IntegerBitsPlusSign<Dst>::value - 1U));
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) << 0U);
+ TEST_EXPECTED_VALUE(2, ClampedNumeric<Dst>(1) << 1U);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) >>
+ as_unsigned(IntegerBitsPlusSign<Dst>::value));
+ TEST_EXPECTED_VALUE(
+ 0, ClampedNumeric<Dst>(1) >> (IntegerBitsPlusSign<Dst>::value - 1U));
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) & 1);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) & 0);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0) & 1);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) & 0);
+ TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
+ MakeClampedNum(DstLimits::max()) & -1);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) | 1);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) | 0);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(0) | 1);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0) | 0);
+ TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
+ ClampedNumeric<Dst>(0) | static_cast<Dst>(-1));
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) ^ 1);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) ^ 0);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(0) ^ 1);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0) ^ 0);
+ TEST_EXPECTED_VALUE(std::numeric_limits<Dst>::max(),
+ ClampedNumeric<Dst>(0) ^ static_cast<Dst>(-1));
+ TEST_EXPECTED_VALUE(DstLimits::max(), ~ClampedNumeric<Dst>(0));
+
TestStrictPointerMath<Dst>();
}
@@ -359,7 +572,7 @@
const char* dst,
int line,
typename std::enable_if<numeric_limits<Dst>::is_iec559, int>::type = 0) {
- using DstLimits = numeric_limits<Dst>;
+ using DstLimits = SaturationDefaultLimits<Dst>;
TEST_EXPECTED_SUCCESS(-CheckedNumeric<Dst>(DstLimits::lowest()));
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::lowest()).Abs());
@@ -378,12 +591,39 @@
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::lowest()) * 2);
TEST_EXPECTED_VALUE(-0.5, CheckedNumeric<Dst>(-1.0) / 2);
+
+ TEST_EXPECTED_VALUE(DstLimits::max(),
+ -ClampedNumeric<Dst>(DstLimits::lowest()));
+
+ TEST_EXPECTED_VALUE(DstLimits::max(),
+ ClampedNumeric<Dst>(DstLimits::lowest()).Abs());
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(-1).Abs());
+
+ TEST_EXPECTED_VALUE(DstLimits::lowest() - 1,
+ ClampedNumeric<Dst>(DstLimits::lowest()) + -1);
+ TEST_EXPECTED_VALUE(DstLimits::max() + 1,
+ ClampedNumeric<Dst>(DstLimits::max()) + 1);
+ TEST_EXPECTED_VALUE(
+ DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) + DstLimits::lowest());
+
+ TEST_EXPECTED_VALUE(
+ DstLimits::Overflow(),
+ ClampedNumeric<Dst>(DstLimits::max()) - DstLimits::lowest());
+ TEST_EXPECTED_VALUE(
+ DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) - DstLimits::max());
+
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::lowest()) * 2);
+
+ TEST_EXPECTED_VALUE(-0.5, ClampedNumeric<Dst>(-1.0) / 2);
}
// Generic arithmetic tests.
template <typename Dst>
static void TestArithmetic(const char* dst, int line) {
- using DstLimits = numeric_limits<Dst>;
+ using DstLimits = SaturationDefaultLimits<Dst>;
EXPECT_EQ(true, CheckedNumeric<Dst>().IsValid());
EXPECT_EQ(false,
@@ -417,6 +657,27 @@
checked_dst = 1;
TEST_EXPECTED_VALUE(1, checked_dst /= 1);
+ TEST_EXPECTED_VALUE(2, ClampedNumeric<Dst>(1) + ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) - ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) * ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) / ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(2, 1 + ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, 1 - ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, 1 * ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, 1 / ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(2, ClampedNumeric<Dst>(1) + 1);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(1) - 1);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) * 1);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) / 1);
+ ClampedNumeric<Dst> clamped_dst = 1;
+ TEST_EXPECTED_VALUE(2, clamped_dst += 1);
+ clamped_dst = 1;
+ TEST_EXPECTED_VALUE(0, clamped_dst -= 1);
+ clamped_dst = 1;
+ TEST_EXPECTED_VALUE(1, clamped_dst *= 1);
+ clamped_dst = 1;
+ TEST_EXPECTED_VALUE(1, clamped_dst /= 1);
+
// Generic negation.
if (DstLimits::is_signed) {
TEST_EXPECTED_VALUE(0, -CheckedNumeric<Dst>());
@@ -424,6 +685,12 @@
TEST_EXPECTED_VALUE(1, -CheckedNumeric<Dst>(-1));
TEST_EXPECTED_VALUE(static_cast<Dst>(DstLimits::max() * -1),
-CheckedNumeric<Dst>(DstLimits::max()));
+
+ TEST_EXPECTED_VALUE(0, -ClampedNumeric<Dst>());
+ TEST_EXPECTED_VALUE(-1, -ClampedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, -ClampedNumeric<Dst>(-1));
+ TEST_EXPECTED_VALUE(static_cast<Dst>(DstLimits::max() * -1),
+ -ClampedNumeric<Dst>(DstLimits::max()));
}
// Generic absolute value.
@@ -432,6 +699,11 @@
TEST_EXPECTED_VALUE(DstLimits::max(),
CheckedNumeric<Dst>(DstLimits::max()).Abs());
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>().Abs());
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1).Abs());
+ TEST_EXPECTED_VALUE(DstLimits::max(),
+ ClampedNumeric<Dst>(DstLimits::max()).Abs());
+
// Generic addition.
TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>() + 1));
TEST_EXPECTED_VALUE(2, (CheckedNumeric<Dst>(1) + 1));
@@ -441,6 +713,15 @@
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) +
DstLimits::max());
+ TEST_EXPECTED_VALUE(1, (ClampedNumeric<Dst>() + 1));
+ TEST_EXPECTED_VALUE(2, (ClampedNumeric<Dst>(1) + 1));
+ if (numeric_limits<Dst>::is_signed)
+ TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>(-1) + 1));
+ TEST_EXPECTED_VALUE(DstLimits::lowest() + 1,
+ ClampedNumeric<Dst>(DstLimits::lowest()) + 1);
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ ClampedNumeric<Dst>(DstLimits::max()) + DstLimits::max());
+
// Generic subtraction.
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(1) - 1));
TEST_EXPECTED_SUCCESS(CheckedNumeric<Dst>(DstLimits::max()) - 1);
@@ -451,6 +732,17 @@
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) - -1);
}
+ TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>(1) - 1));
+ TEST_EXPECTED_VALUE(DstLimits::max() - 1,
+ ClampedNumeric<Dst>(DstLimits::max()) - 1);
+ if (numeric_limits<Dst>::is_signed) {
+ TEST_EXPECTED_VALUE(-1, (ClampedNumeric<Dst>() - 1));
+ TEST_EXPECTED_VALUE(-2, (ClampedNumeric<Dst>(-1) - 1));
+ } else {
+ TEST_EXPECTED_VALUE(DstLimits::max(),
+ ClampedNumeric<Dst>(DstLimits::max()) - -1);
+ }
+
// Generic multiplication.
TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>() * 1));
TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>(1) * 1));
@@ -467,6 +759,22 @@
TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(DstLimits::max()) *
DstLimits::max());
+ TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>() * 1));
+ TEST_EXPECTED_VALUE(1, (ClampedNumeric<Dst>(1) * 1));
+ TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>(0) * 0));
+ if (numeric_limits<Dst>::is_signed) {
+ TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>(-1) * 0));
+ TEST_EXPECTED_VALUE(0, (ClampedNumeric<Dst>(0) * -1));
+ TEST_EXPECTED_VALUE(-2, (ClampedNumeric<Dst>(-1) * 2));
+ } else {
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ ClampedNumeric<Dst>(DstLimits::max()) * -2);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(DstLimits::max()) *
+ ClampedNumeric<uintmax_t>(-2));
+ }
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ ClampedNumeric<Dst>(DstLimits::max()) * DstLimits::max());
+
// Generic division.
TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() / 1);
TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1);
@@ -474,6 +782,17 @@
CheckedNumeric<Dst>(DstLimits::lowest()) / 2);
TEST_EXPECTED_VALUE(DstLimits::max() / 2,
CheckedNumeric<Dst>(DstLimits::max()) / 2);
+ TEST_EXPECTED_FAILURE(CheckedNumeric<Dst>(1) / 0);
+
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>() / 1);
+ TEST_EXPECTED_VALUE(1, ClampedNumeric<Dst>(1) / 1);
+ TEST_EXPECTED_VALUE(DstLimits::lowest() / 2,
+ ClampedNumeric<Dst>(DstLimits::lowest()) / 2);
+ TEST_EXPECTED_VALUE(DstLimits::max() / 2,
+ ClampedNumeric<Dst>(DstLimits::max()) / 2);
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(), ClampedNumeric<Dst>(1) / 0);
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(), ClampedNumeric<Dst>(-1) / 0);
+ TEST_EXPECTED_VALUE(0, ClampedNumeric<Dst>(0) / 0);
TestSpecializedArithmetic<Dst>(dst, line);
}
@@ -605,8 +924,8 @@
template <typename Dst, typename Src>
struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> {
static void Test(const char *dst, const char *src, int line) {
- using SrcLimits = numeric_limits<Src>;
- using DstLimits = numeric_limits<Dst>;
+ using SrcLimits = SaturationDefaultLimits<Src>;
+ using DstLimits = SaturationDefaultLimits<Dst>;
// Integral to floating.
static_assert((DstLimits::is_iec559 && SrcLimits::is_integer) ||
// Not floating to integral and...
@@ -622,18 +941,26 @@
TestStrictComparison<Dst, Src>();
const CheckedNumeric<Dst> checked_dst = SrcLimits::max();
+ const ClampedNumeric<Dst> clamped_dst = SrcLimits::max();
TEST_EXPECTED_SUCCESS(checked_dst);
+ TEST_EXPECTED_VALUE(Dst(SrcLimits::max()), clamped_dst);
if (MaxExponent<Dst>::value > MaxExponent<Src>::value) {
if (MaxExponent<Dst>::value >= MaxExponent<Src>::value * 2 - 1) {
// At least twice larger type.
TEST_EXPECTED_SUCCESS(SrcLimits::max() * checked_dst);
-
+ TEST_EXPECTED_VALUE(SrcLimits::max() * clamped_dst,
+ Dst(SrcLimits::max()) * SrcLimits::max());
} else { // Larger, but not at least twice as large.
TEST_EXPECTED_FAILURE(SrcLimits::max() * checked_dst);
TEST_EXPECTED_SUCCESS(checked_dst + 1);
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(),
+ SrcLimits::max() * clamped_dst);
+ TEST_EXPECTED_VALUE(Dst(SrcLimits::max()) + Dst(1),
+ clamped_dst + Dst(1));
}
} else { // Same width type.
TEST_EXPECTED_FAILURE(checked_dst + 1);
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst + Dst(1));
}
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max());
@@ -653,8 +980,8 @@
template <typename Dst, typename Src>
struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> {
static void Test(const char *dst, const char *src, int line) {
- using SrcLimits = numeric_limits<Src>;
- using DstLimits = numeric_limits<Dst>;
+ using SrcLimits = SaturationDefaultLimits<Src>;
+ using DstLimits = SaturationDefaultLimits<Dst>;
static_assert(SrcLimits::is_signed == DstLimits::is_signed,
"Destination and source sign must be the same");
static_assert(MaxExponent<Dst>::value <= MaxExponent<Src>::value,
@@ -664,9 +991,14 @@
const CheckedNumeric<Dst> checked_dst;
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
- TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
+ TEST_EXPECTED_VALUE(1, checked_dst + Src(1));
TEST_EXPECTED_FAILURE(checked_dst - SrcLimits::max());
+ const ClampedNumeric<Dst> clamped_dst;
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst + SrcLimits::max());
+ TEST_EXPECTED_VALUE(1, clamped_dst + Src(1));
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(), clamped_dst - SrcLimits::max());
+
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
if (SrcLimits::is_iec559) {
@@ -689,10 +1021,12 @@
}
} else if (SrcLimits::is_signed) {
TEST_EXPECTED_VALUE(-1, checked_dst - static_cast<Src>(1));
+ TEST_EXPECTED_VALUE(-1, clamped_dst - static_cast<Src>(1));
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
} else {
TEST_EXPECTED_FAILURE(checked_dst - static_cast<Src>(1));
+ TEST_EXPECTED_VALUE(Dst(0), clamped_dst - static_cast<Src>(1));
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
}
}
@@ -701,8 +1035,8 @@
template <typename Dst, typename Src>
struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL> {
static void Test(const char *dst, const char *src, int line) {
- using SrcLimits = numeric_limits<Src>;
- using DstLimits = numeric_limits<Dst>;
+ using SrcLimits = SaturationDefaultLimits<Src>;
+ using DstLimits = SaturationDefaultLimits<Dst>;
static_assert(MaxExponent<Dst>::value >= MaxExponent<Src>::value,
"Destination must be equal or wider than source.");
static_assert(SrcLimits::is_signed, "Source must be signed");
@@ -713,8 +1047,17 @@
const CheckedNumeric<Dst> checked_dst;
TEST_EXPECTED_VALUE(SrcLimits::max(), checked_dst + SrcLimits::max());
TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1));
+ TEST_EXPECTED_SUCCESS(checked_dst * static_cast<Src>(-1));
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::lowest());
+ const ClampedNumeric<Dst> clamped_dst;
+ TEST_EXPECTED_VALUE(SrcLimits::max(), clamped_dst + SrcLimits::max());
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ clamped_dst + static_cast<Src>(-1));
+ TEST_EXPECTED_VALUE(0, clamped_dst * static_cast<Src>(-1));
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ clamped_dst + SrcLimits::lowest());
+
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::lowest());
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
@@ -725,8 +1068,8 @@
template <typename Dst, typename Src>
struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> {
static void Test(const char *dst, const char *src, int line) {
- using SrcLimits = numeric_limits<Src>;
- using DstLimits = numeric_limits<Dst>;
+ using SrcLimits = SaturationDefaultLimits<Src>;
+ using DstLimits = SaturationDefaultLimits<Dst>;
static_assert(MaxExponent<Dst>::value < MaxExponent<Src>::value,
"Destination must be narrower than source.");
static_assert(SrcLimits::is_signed, "Source must be signed.");
@@ -740,12 +1083,20 @@
TEST_EXPECTED_FAILURE(checked_dst + static_cast<Src>(-1));
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::lowest());
+ const ClampedNumeric<Dst> clamped_dst;
+ TEST_EXPECTED_VALUE(1, clamped_dst + static_cast<Src>(1));
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst + SrcLimits::max());
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ clamped_dst + static_cast<Src>(-1));
+ TEST_EXPECTED_VALUE(DstLimits::Underflow(),
+ clamped_dst + SrcLimits::lowest());
+
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
// Additional saturation tests.
- EXPECT_EQ(DstLimits::max(), saturated_cast<Dst>(SrcLimits::max())) << src;
+ EXPECT_EQ(DstLimits::max(), saturated_cast<Dst>(SrcLimits::max()));
EXPECT_EQ(DstLimits::lowest(), saturated_cast<Dst>(SrcLimits::lowest()));
if (SrcLimits::is_iec559) {
@@ -776,8 +1127,8 @@
template <typename Dst, typename Src>
struct TestNumericConversion<Dst, Src, UNSIGN_TO_SIGN_NARROW_OR_EQUAL> {
static void Test(const char *dst, const char *src, int line) {
- using SrcLimits = numeric_limits<Src>;
- using DstLimits = numeric_limits<Dst>;
+ using SrcLimits = SaturationDefaultLimits<Src>;
+ using DstLimits = SaturationDefaultLimits<Dst>;
static_assert(MaxExponent<Dst>::value <= MaxExponent<Src>::value,
"Destination must be narrower or equal to source.");
static_assert(!SrcLimits::is_signed, "Source must be unsigned.");
@@ -790,6 +1141,11 @@
TEST_EXPECTED_FAILURE(checked_dst + SrcLimits::max());
TEST_EXPECTED_VALUE(SrcLimits::lowest(), checked_dst + SrcLimits::lowest());
+ const ClampedNumeric<Dst> clamped_dst;
+ TEST_EXPECTED_VALUE(1, clamped_dst + static_cast<Src>(1));
+ TEST_EXPECTED_VALUE(DstLimits::Overflow(), clamped_dst + SrcLimits::max());
+ TEST_EXPECTED_VALUE(SrcLimits::lowest(), clamped_dst + SrcLimits::lowest());
+
TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::lowest());
TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
@@ -1167,20 +1523,41 @@
}
TEST(SafeNumerics, VariadicNumericOperations) {
- auto a = CheckAdd(1, 2UL, MakeCheckedNum(3LL), 4).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(a)::type>(10), a);
- auto b = CheckSub(MakeCheckedNum(20.0), 2UL, 4).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(b)::type>(14.0), b);
- auto c = CheckMul(20.0, MakeCheckedNum(1), 5, 3UL).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(c)::type>(300.0), c);
- auto d = CheckDiv(20.0, 2.0, MakeCheckedNum(5LL), -4).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(d)::type>(-.5), d);
- auto e = CheckMod(MakeCheckedNum(20), 3).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(e)::type>(2), e);
- auto f = CheckLsh(1, MakeCheckedNum(2)).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(f)::type>(4), f);
- auto g = CheckRsh(4, MakeCheckedNum(2)).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(g)::type>(1), g);
- auto h = CheckRsh(CheckAdd(1, 1, 1, 1), CheckSub(4, 2)).ValueOrDie();
- EXPECT_EQ(static_cast<decltype(h)::type>(1), h);
+ { // Synthetic scope to avoid variable naming collisions.
+ auto a = CheckAdd(1, 2UL, MakeCheckedNum(3LL), 4).ValueOrDie();
+ EXPECT_EQ(static_cast<decltype(a)::type>(10), a);
+ auto b = CheckSub(MakeCheckedNum(20.0), 2UL, 4).ValueOrDie();
+ EXPECT_EQ(static_cast<decltype(b)::type>(14.0), b);
+ auto c = CheckMul(20.0, MakeCheckedNum(1), 5, 3UL).ValueOrDie();
+ EXPECT_EQ(static_cast<decltype(c)::type>(300.0), c);
+ auto d = CheckDiv(20.0, 2.0, MakeCheckedNum(5LL), -4).ValueOrDie();
+ EXPECT_EQ(static_cast<decltype(d)::type>(-.5), d);
+ auto e = CheckMod(MakeCheckedNum(20), 3).ValueOrDie();
+ EXPECT_EQ(static_cast<decltype(e)::type>(2), e);
+ auto f = CheckLsh(1, MakeCheckedNum(2)).ValueOrDie();
+ EXPECT_EQ(static_cast<decltype(f)::type>(4), f);
+ auto g = CheckRsh(4, MakeCheckedNum(2)).ValueOrDie();
+ EXPECT_EQ(static_cast<decltype(g)::type>(1), g);
+ auto h = CheckRsh(CheckAdd(1, 1, 1, 1), CheckSub(4, 2)).ValueOrDie();
+ EXPECT_EQ(static_cast<decltype(h)::type>(1), h);
+ }
+
+ {
+ auto a = ClampAdd(1, 2UL, MakeClampedNum(3LL), 4);
+ EXPECT_EQ(static_cast<decltype(a)::type>(10), a);
+ auto b = ClampSub(MakeClampedNum(20.0), 2UL, 4);
+ EXPECT_EQ(static_cast<decltype(b)::type>(14.0), b);
+ auto c = ClampMul(20.0, MakeClampedNum(1), 5, 3UL);
+ EXPECT_EQ(static_cast<decltype(c)::type>(300.0), c);
+ auto d = ClampDiv(20.0, 2.0, MakeClampedNum(5LL), -4);
+ EXPECT_EQ(static_cast<decltype(d)::type>(-.5), d);
+ auto e = ClampMod(MakeClampedNum(20), 3);
+ EXPECT_EQ(static_cast<decltype(e)::type>(2), e);
+ auto f = ClampLsh(1, MakeClampedNum(2U));
+ EXPECT_EQ(static_cast<decltype(f)::type>(4), f);
+ auto g = ClampRsh(4, MakeClampedNum(2U));
+ EXPECT_EQ(static_cast<decltype(g)::type>(1), g);
+ auto h = ClampRsh(ClampAdd(1, 1, 1, 1), ClampSub(4U, 2));
+ EXPECT_EQ(static_cast<decltype(h)::type>(1), h);
+ }
}