Andrew Top | 0d1858f | 2019-05-15 22:01:47 -0700 | [diff] [blame] | 1 | # `base/numerics` |
| 2 | |
| 3 | This directory contains a dependency-free, header-only library of templates |
| 4 | providing well-defined semantics for safely and performantly handling a variety |
| 5 | of numeric operations, including most common arithmetic operations and |
| 6 | conversions. |
| 7 | |
| 8 | The public API is broken out into the following header files: |
| 9 | |
| 10 | * `checked_math.h` contains the `CheckedNumeric` template class and helper |
| 11 | functions for performing arithmetic and conversion operations that detect |
| 12 | errors and boundary conditions (e.g. overflow, truncation, etc.). |
| 13 | * `clamped_math.h` contains the `ClampedNumeric` template class and |
| 14 | helper functions for performing fast, clamped (i.e. [non-sticky](#notsticky) |
| 15 | saturating) arithmetic operations and conversions. |
| 16 | * `safe_conversions.h` contains the `StrictNumeric` template class and |
| 17 | a collection of custom casting templates and helper functions for safely |
| 18 | converting between a range of numeric types. |
| 19 | * `safe_math.h` includes all of the previously mentioned headers. |
| 20 | |
| 21 | *** aside |
| 22 | **Note:** The `Numeric` template types implicitly convert from C numeric types |
| 23 | and `Numeric` templates that are convertable to an underlying C numeric type. |
| 24 | The conversion priority for `Numeric` type coercions is: |
| 25 | |
| 26 | * `StrictNumeric` coerces to `ClampedNumeric` and `CheckedNumeric` |
| 27 | * `ClampedNumeric` coerces to `CheckedNumeric` |
| 28 | *** |
| 29 | |
| 30 | [TOC] |
| 31 | |
| 32 | ## Common patterns and use-cases |
| 33 | |
| 34 | The following covers the preferred style for the most common uses of this |
| 35 | library. Please don't cargo-cult from anywhere else. 😉 |
| 36 | |
| 37 | ### Performing checked arithmetic type conversions |
| 38 | |
| 39 | The `checked_cast` template converts between arbitrary arithmetic types, and is |
| 40 | used for cases where a conversion failure should result in program termination: |
| 41 | |
| 42 | ```cpp |
| 43 | // Crash if signed_value is out of range for buff_size. |
| 44 | size_t buff_size = checked_cast<size_t>(signed_value); |
| 45 | ``` |
| 46 | |
| 47 | ### Performing saturated (clamped) arithmetic type conversions |
| 48 | |
| 49 | The `saturated_cast` template converts between arbitrary arithmetic types, and |
| 50 | is used in cases where an out-of-bounds source value should be saturated to the |
| 51 | corresponding maximum or minimum of the destination type: |
| 52 | |
| 53 | ```cpp |
| 54 | // Convert from float with saturation to INT_MAX, INT_MIN, or 0 for NaN. |
| 55 | int int_value = saturated_cast<int>(floating_point_value); |
| 56 | ``` |
| 57 | |
| 58 | ### Enforcing arithmetic type conversions at compile-time |
| 59 | |
| 60 | The `strict_cast` emits code that is identical to `static_cast`. However, |
| 61 | provides static checks that will cause a compilation failure if the |
| 62 | destination type cannot represent the full range of the source type: |
| 63 | |
| 64 | ```cpp |
| 65 | // Throw a compiler error if byte_value is changed to an out-of-range-type. |
| 66 | int int_value = strict_cast<int>(byte_value); |
| 67 | ``` |
| 68 | |
| 69 | You can also enforce these compile-time restrictions on function parameters by |
| 70 | using the `StrictNumeric` template: |
| 71 | |
| 72 | ```cpp |
| 73 | // Throw a compiler error if the size argument cannot be represented by a |
| 74 | // size_t (e.g. passing an int will fail to compile). |
| 75 | bool AllocateBuffer(void** buffer, StrictCast<size_t> size); |
| 76 | ``` |
| 77 | |
| 78 | ### Comparing values between arbitrary arithmetic types |
| 79 | |
| 80 | Both the `StrictNumeric` and `ClampedNumeric` types provide well defined |
| 81 | comparisons between arbitrary arithmetic types. This allows you to perform |
| 82 | comparisons that are not legal or would trigger compiler warnings or errors |
| 83 | under the normal arithmetic promotion rules: |
| 84 | |
| 85 | ```cpp |
| 86 | bool foo(unsigned value, int upper_bound) { |
| 87 | // Converting to StrictNumeric allows this comparison to work correctly. |
| 88 | if (MakeStrictNum(value) >= upper_bound) |
| 89 | return false; |
| 90 | ``` |
| 91 | |
| 92 | *** note |
| 93 | **Warning:** Do not perform manual conversions using the comparison operators. |
| 94 | Instead, use the cast templates described in the previous sections, or the |
| 95 | constexpr template functions `IsValueInRangeForNumericType` and |
| 96 | `IsTypeInRangeForNumericType`, as these templates properly handle the full range |
| 97 | of corner cases and employ various optimizations. |
| 98 | *** |
| 99 | |
| 100 | ### Calculating a buffer size (checked arithmetic) |
| 101 | |
| 102 | When making exact calculations—such as for buffer lengths—it's often necessary |
| 103 | to know when those calculations trigger an overflow, undefined behavior, or |
| 104 | other boundary conditions. The `CheckedNumeric` template does this by storing |
| 105 | a bit determining whether or not some arithmetic operation has occured that |
| 106 | would put the variable in an "invalid" state. Attempting to extract the value |
| 107 | from a variable in an invalid state will trigger a check/trap condition, that |
| 108 | by default will result in process termination. |
| 109 | |
| 110 | Here's an example of a buffer calculation using a `CheckedNumeric` type (note: |
| 111 | the AssignIfValid method will trigger a compile error if the result is ignored). |
| 112 | |
| 113 | ```cpp |
| 114 | // Calculate the buffer size and detect if an overflow occurs. |
| 115 | size_t size; |
| 116 | if (!CheckAdd(kHeaderSize, CheckMul(count, kItemSize)).AssignIfValid(&size)) { |
| 117 | // Handle an overflow error... |
| 118 | } |
| 119 | ``` |
| 120 | |
| 121 | ### Calculating clamped coordinates (non-sticky saturating arithmetic) |
| 122 | |
| 123 | Certain classes of calculations—such as coordinate calculations—require |
| 124 | well-defined semantics that always produce a valid result on boundary |
| 125 | conditions. The `ClampedNumeric` template addresses this by providing |
| 126 | performant, non-sticky saturating arithmetic operations. |
| 127 | |
| 128 | Here's an example of using a `ClampedNumeric` to calculate an operation |
| 129 | insetting a rectangle. |
| 130 | |
| 131 | ```cpp |
| 132 | // Use clamped arithmetic since inset calculations might overflow. |
| 133 | void Rect::Inset(int left, int top, int right, int bottom) { |
| 134 | origin_ += Vector2d(left, top); |
| 135 | set_width(ClampSub(width(), ClampAdd(left, right))); |
| 136 | set_height(ClampSub(height(), ClampAdd(top, bottom))); |
| 137 | } |
| 138 | ``` |
| 139 | |
| 140 | *** note |
| 141 | <a name="notsticky"></a> |
| 142 | The `ClampedNumeric` type is not "sticky", which means the saturation is not |
| 143 | retained across individual operations. As such, one arithmetic operation may |
| 144 | result in a saturated value, while the next operation may then "desaturate" |
| 145 | the value. Here's an example: |
| 146 | |
| 147 | ```cpp |
| 148 | ClampedNumeric<int> value = INT_MAX; |
| 149 | ++value; // value is still INT_MAX, due to saturation. |
| 150 | --value; // value is now (INT_MAX - 1), because saturation is not sticky. |
| 151 | ``` |
| 152 | |
| 153 | *** |
| 154 | |
| 155 | ## Conversion functions and StrictNumeric<> in safe_conversions.h |
| 156 | |
| 157 | This header includes a collection of helper `constexpr` templates for safely |
| 158 | performing a range of conversions, assignments, and tests. |
| 159 | |
| 160 | ### Safe casting templates |
| 161 | |
| 162 | * `as_signed()` - Returns the supplied integral value as a signed type of |
| 163 | the same width. |
| 164 | * `as_unsigned()` - Returns the supplied integral value as an unsigned type |
| 165 | of the same width. |
| 166 | * `checked_cast<>()` - Analogous to `static_cast<>` for numeric types, except |
| 167 | that by default it will trigger a crash on an out-of-bounds conversion (e.g. |
| 168 | overflow, underflow, NaN to integral) or a compile error if the conversion |
| 169 | error can be detected at compile time. The crash handler can be overridden |
| 170 | to perform a behavior other than crashing. |
| 171 | * `saturated_cast<>()` - Analogous to `static_cast` for numeric types, except |
| 172 | that it returns a saturated result when the specified numeric conversion |
| 173 | would otherwise overflow or underflow. An NaN source returns 0 by |
| 174 | default, but can be overridden to return a different result. |
| 175 | * `strict_cast<>()` - Analogous to `static_cast` for numeric types, except |
| 176 | this causes a compile failure if the destination type is not large |
| 177 | enough to contain any value in the source type. It performs no runtime |
| 178 | checking and thus introduces no runtime overhead. |
| 179 | |
| 180 | ### Other helper and conversion functions |
| 181 | |
| 182 | * `IsValueInRangeForNumericType<>()` - A convenience function that returns |
| 183 | true if the type supplied as the template parameter can represent the value |
| 184 | passed as an argument to the function. |
| 185 | * `IsTypeInRangeForNumericType<>()` - A convenience function that evaluates |
| 186 | entirely at compile-time and returns true if the destination type (first |
| 187 | template parameter) can represent the full range of the source type |
| 188 | (second template parameter). |
| 189 | * `IsValueNegative()` - A convenience function that will accept any |
| 190 | arithmetic type as an argument and will return whether the value is less |
| 191 | than zero. Unsigned types always return false. |
| 192 | * `SafeUnsignedAbs()` - Returns the absolute value of the supplied integer |
| 193 | parameter as an unsigned result (thus avoiding an overflow if the value |
| 194 | is the signed, two's complement minimum). |
| 195 | |
| 196 | ### StrictNumeric<> |
| 197 | |
| 198 | `StrictNumeric<>` is a wrapper type that performs assignments and copies via |
| 199 | the `strict_cast` template, and can perform valid arithmetic comparisons |
| 200 | across any range of arithmetic types. `StrictNumeric` is the return type for |
| 201 | values extracted from a `CheckedNumeric` class instance. The raw numeric value |
| 202 | is extracted via `static_cast` to the underlying type or any type with |
| 203 | sufficient range to represent the underlying type. |
| 204 | |
| 205 | * `MakeStrictNum()` - Creates a new `StrictNumeric` from the underlying type |
| 206 | of the supplied arithmetic or StrictNumeric type. |
| 207 | * `SizeT` - Alias for `StrictNumeric<size_t>`. |
| 208 | |
| 209 | ## CheckedNumeric<> in checked_math.h |
| 210 | |
| 211 | `CheckedNumeric<>` implements all the logic and operators for detecting integer |
| 212 | boundary conditions such as overflow, underflow, and invalid conversions. |
| 213 | The `CheckedNumeric` type implicitly converts from floating point and integer |
| 214 | data types, and contains overloads for basic arithmetic operations (i.e.: `+`, |
| 215 | `-`, `*`, `/` for all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers). |
| 216 | However, *the [variadic template functions |
| 217 | ](#CheckedNumeric_in-checked_math_h-Non_member-helper-functions) |
| 218 | are the prefered API,* as they remove type ambiguities and help prevent a number |
| 219 | of common errors. The variadic functions can also be more performant, as they |
| 220 | eliminate redundant expressions that are unavoidable with the with the operator |
| 221 | overloads. (Ideally the compiler should optimize those away, but better to avoid |
| 222 | them in the first place.) |
| 223 | |
| 224 | Type promotions are a slightly modified version of the [standard C/C++ numeric |
| 225 | promotions |
| 226 | ](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions) |
| 227 | with the two differences being that *there is no default promotion to int* |
| 228 | and *bitwise logical operations always return an unsigned of the wider type.* |
| 229 | |
| 230 | ### Members |
| 231 | |
| 232 | The unary negation, increment, and decrement operators are supported, along |
| 233 | with the following unary arithmetic methods, which return a new |
| 234 | `CheckedNumeric` as a result of the operation: |
| 235 | |
| 236 | * `Abs()` - Absolute value. |
| 237 | * `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type |
| 238 | (valid for only integral types). |
| 239 | * `Max()` - Returns whichever is greater of the current instance or argument. |
| 240 | The underlying return type is whichever has the greatest magnitude. |
| 241 | * `Min()` - Returns whichever is lowest of the current instance or argument. |
| 242 | The underlying return type is whichever has can represent the lowest |
| 243 | number in the smallest width (e.g. int8_t over unsigned, int over |
| 244 | int8_t, and float over int). |
| 245 | |
| 246 | The following are for converting `CheckedNumeric` instances: |
| 247 | |
| 248 | * `type` - The underlying numeric type. |
| 249 | * `AssignIfValid()` - Assigns the underlying value to the supplied |
| 250 | destination pointer if the value is currently valid and within the |
| 251 | range supported by the destination type. Returns true on success. |
| 252 | * `Cast<>()` - Instance method returning a `CheckedNumeric` derived from |
| 253 | casting the current instance to a `CheckedNumeric` of the supplied |
| 254 | destination type. |
| 255 | |
| 256 | *** aside |
| 257 | The following member functions return a `StrictNumeric`, which is valid for |
| 258 | comparison and assignment operations, but will trigger a compile failure on |
| 259 | attempts to assign to a type of insufficient range. The underlying value can |
| 260 | be extracted by an explicit `static_cast` to the underlying type or any type |
| 261 | with sufficient range to represent the underlying type. |
| 262 | *** |
| 263 | |
| 264 | * `IsValid()` - Returns true if the underlying numeric value is valid (i.e. |
| 265 | has not wrapped or saturated and is not the result of an invalid |
| 266 | conversion). |
| 267 | * `ValueOrDie()` - Returns the underlying value. If the state is not valid |
| 268 | this call will trigger a crash by default (but may be overridden by |
| 269 | supplying an alternate handler to the template). |
| 270 | * `ValueOrDefault()` - Returns the current value, or the supplied default if |
| 271 | the state is not valid (but will not crash). |
| 272 | |
| 273 | **Comparison operators are explicitly not provided** for `CheckedNumeric` |
| 274 | types because they could result in a crash if the type is not in a valid state. |
| 275 | Patterns like the following should be used instead: |
| 276 | |
| 277 | ```cpp |
| 278 | // Either input or padding (or both) may be arbitrary sizes. |
| 279 | size_t buff_size; |
| 280 | if (!CheckAdd(input, padding, kHeaderLength).AssignIfValid(&buff_size) || |
| 281 | buff_size >= kMaxBuffer) { |
| 282 | // Handle an error... |
| 283 | } else { |
| 284 | // Do stuff on success... |
| 285 | } |
| 286 | ``` |
| 287 | |
| 288 | ### Non-member helper functions |
| 289 | |
| 290 | The following variadic convenience functions, which accept standard arithmetic |
| 291 | or `CheckedNumeric` types, perform arithmetic operations, and return a |
| 292 | `CheckedNumeric` result. The supported functions are: |
| 293 | |
| 294 | * `CheckAdd()` - Addition. |
| 295 | * `CheckSub()` - Subtraction. |
| 296 | * `CheckMul()` - Multiplication. |
| 297 | * `CheckDiv()` - Division. |
| 298 | * `CheckMod()` - Modulus (integer only). |
| 299 | * `CheckLsh()` - Left integer shift (integer only). |
| 300 | * `CheckRsh()` - Right integer shift (integer only). |
| 301 | * `CheckAnd()` - Bitwise AND (integer only with unsigned result). |
| 302 | * `CheckOr()` - Bitwise OR (integer only with unsigned result). |
| 303 | * `CheckXor()` - Bitwise XOR (integer only with unsigned result). |
| 304 | * `CheckMax()` - Maximum of supplied arguments. |
| 305 | * `CheckMin()` - Minimum of supplied arguments. |
| 306 | |
| 307 | The following wrapper functions can be used to avoid the template |
| 308 | disambiguator syntax when converting a destination type. |
| 309 | |
| 310 | * `IsValidForType<>()` in place of: `a.template IsValid<>()` |
| 311 | * `ValueOrDieForType<>()` in place of: `a.template ValueOrDie<>()` |
| 312 | * `ValueOrDefaultForType<>()` in place of: `a.template ValueOrDefault<>()` |
| 313 | |
| 314 | The following general utility methods is are useful for converting from |
| 315 | arithmetic types to `CheckedNumeric` types: |
| 316 | |
| 317 | * `MakeCheckedNum()` - Creates a new `CheckedNumeric` from the underlying type |
| 318 | of the supplied arithmetic or directly convertible type. |
| 319 | |
| 320 | ## ClampedNumeric<> in clamped_math.h |
| 321 | |
| 322 | `ClampedNumeric<>` implements all the logic and operators for clamped |
| 323 | (non-sticky saturating) arithmetic operations and conversions. The |
| 324 | `ClampedNumeric` type implicitly converts back and forth between floating point |
| 325 | and integer data types, saturating on assignment as appropriate. It contains |
| 326 | overloads for basic arithmetic operations (i.e.: `+`, `-`, `*`, `/` for |
| 327 | all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers) along with comparison |
| 328 | operators for arithmetic types of any size. However, *the [variadic template |
| 329 | functions |
| 330 | ](#ClampedNumeric_in-clamped_math_h-Non_member-helper-functions) |
| 331 | are the prefered API,* as they remove type ambiguities and help prevent |
| 332 | a number of common errors. The variadic functions can also be more performant, |
| 333 | as they eliminate redundant expressions that are unavoidable with the operator |
| 334 | overloads. (Ideally the compiler should optimize those away, but better to avoid |
| 335 | them in the first place.) |
| 336 | |
| 337 | Type promotions are a slightly modified version of the [standard C/C++ numeric |
| 338 | promotions |
| 339 | ](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions) |
| 340 | with the two differences being that *there is no default promotion to int* |
| 341 | and *bitwise logical operations always return an unsigned of the wider type.* |
| 342 | |
| 343 | *** aside |
| 344 | Most arithmetic operations saturate normally, to the numeric limit in the |
| 345 | direction of the sign. The potentially unusual cases are: |
| 346 | |
| 347 | * **Division:** Division by zero returns the saturated limit in the direction |
| 348 | of sign of the dividend (first argument). The one exception is 0/0, which |
| 349 | returns zero (although logically is NaN). |
| 350 | * **Modulus:** Division by zero returns the dividend (first argument). |
| 351 | * **Left shift:** Non-zero values saturate in the direction of the signed |
| 352 | limit (max/min), even for shifts larger than the bit width. 0 shifted any |
| 353 | amount results in 0. |
| 354 | * **Right shift:** Negative values saturate to -1. Positive or 0 saturates |
| 355 | to 0. (Effectively just an unbounded arithmetic-right-shift.) |
| 356 | * **Bitwise operations:** No saturation; bit pattern is identical to |
| 357 | non-saturated bitwise operations. |
| 358 | *** |
| 359 | |
| 360 | ### Members |
| 361 | |
| 362 | The unary negation, increment, and decrement operators are supported, along |
| 363 | with the following unary arithmetic methods, which return a new |
| 364 | `ClampedNumeric` as a result of the operation: |
| 365 | |
| 366 | * `Abs()` - Absolute value. |
| 367 | * `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type |
| 368 | (valid for only integral types). |
| 369 | * `Max()` - Returns whichever is greater of the current instance or argument. |
| 370 | The underlying return type is whichever has the greatest magnitude. |
| 371 | * `Min()` - Returns whichever is lowest of the current instance or argument. |
| 372 | The underlying return type is whichever has can represent the lowest |
| 373 | number in the smallest width (e.g. int8_t over unsigned, int over |
| 374 | int8_t, and float over int). |
| 375 | |
| 376 | The following are for converting `ClampedNumeric` instances: |
| 377 | |
| 378 | * `type` - The underlying numeric type. |
| 379 | * `RawValue()` - Returns the raw value as the underlying arithmetic type. This |
| 380 | is useful when e.g. assigning to an auto type or passing as a deduced |
| 381 | template parameter. |
| 382 | * `Cast<>()` - Instance method returning a `ClampedNumeric` derived from |
| 383 | casting the current instance to a `ClampedNumeric` of the supplied |
| 384 | destination type. |
| 385 | |
| 386 | ### Non-member helper functions |
| 387 | |
| 388 | The following variadic convenience functions, which accept standard arithmetic |
| 389 | or `ClampedNumeric` types, perform arithmetic operations, and return a |
| 390 | `ClampedNumeric` result. The supported functions are: |
| 391 | |
| 392 | * `ClampAdd()` - Addition. |
| 393 | * `ClampSub()` - Subtraction. |
| 394 | * `ClampMul()` - Multiplication. |
| 395 | * `ClampDiv()` - Division. |
| 396 | * `ClampMod()` - Modulus (integer only). |
| 397 | * `ClampLsh()` - Left integer shift (integer only). |
| 398 | * `ClampRsh()` - Right integer shift (integer only). |
| 399 | * `ClampAnd()` - Bitwise AND (integer only with unsigned result). |
| 400 | * `ClampOr()` - Bitwise OR (integer only with unsigned result). |
| 401 | * `ClampXor()` - Bitwise XOR (integer only with unsigned result). |
| 402 | * `ClampMax()` - Maximum of supplied arguments. |
| 403 | * `ClampMin()` - Minimum of supplied arguments. |
| 404 | |
| 405 | The following is a general utility method that is useful for converting |
| 406 | to a `ClampedNumeric` type: |
| 407 | |
| 408 | * `MakeClampedNum()` - Creates a new `ClampedNumeric` from the underlying type |
| 409 | of the supplied arithmetic or directly convertible type. |