README.md
1# `base/numerics`
2
3This directory contains a dependency-free, header-only library of templates
4providing well-defined semantics for safely and performantly handling a variety
5of numeric operations, including most common arithmetic operations and
6conversions.
7
8The 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 saturating)
15 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
23and `Numeric` templates that are convertable to an underlying C numeric type.
24The 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
34The following covers the preferred style for the most common uses of this
35library. Please don't cargo-cult from anywhere else.
36
37### Performing checked arithmetic conversions
38
39The `checked_cast` template converts between arbitrary arithmetic types, and is
40used 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.
44size_t buff_size = checked_cast<size_t>(signed_value);
45```
46
47### Performing saturated (clamped) arithmetic conversions
48
49The `saturated_cast` template converts between arbitrary arithmetic types, and
50is used in cases where an out-of-bounds source value should be saturated to the
51corresponding 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.
55int int_value = saturated_cast<int>(floating_point_value);
56```
57
58### Enforcing arithmetic conversions at compile-time
59
60The `strict_cast` enforces type restrictions at compile-time and results in
61emitted code that is identical to a normal `static_cast`. However, a
62`strict_cast` assignment will fail to compile if the destination type cannot
63represent the full range of the source type:
64
65```cpp
66// Throw a compiler error if byte_value is changed to an out-of-range-type.
67int int_value = strict_cast<int>(byte_value);
68```
69
70You can also enforce these compile-time restrictions on function parameters by
71using the `StrictNumeric` template:
72
73```cpp
74// Throw a compiler error if the size argument cannot be represented by a
75// size_t (e.g. passing an int will fail to compile).
76bool AllocateBuffer(void** buffer, StrictCast<size_t> size);
77```
78
79### Comparing values between arbitrary arithmetic types
80
81Both the `StrictNumeric` and `ClampedNumeric` types provide well defined
82comparisons between arbitrary arithmetic types. This allows you to perform
83comparisons that are not legal or would trigger compiler warnings or errors
84under the normal arithmetic promotion rules:
85
86```cpp
87bool foo(unsigned value, int upper_bound) {
88 // Converting to StrictNumeric allows this comparison to work correctly.
89 if (MakeStrictNum(value) >= upper_bound)
90 return false;
91```
92
93*** note
94**Warning:** Do not perform manual conversions using the comparison operators.
95Instead, use the cast templates described in the previous sections, or the
96constexpr template functions `IsValueInRangeForNumericType` and
97`IsTypeInRangeForNumericType`, as these templates properly handle the full range
98of corner cases and employ various optimizations.
99***
100
101### Calculating a buffer size (checked arithmetic)
102
103When making exact calculations—such as for buffer lengths—it's often necessary
104to know when those calculations trigger an overflow, undefined behavior, or
105other boundary conditions. The `CheckedNumeric` template does this by storing
106a bit determining whether or not some arithmetic operation has occured that
107would put the variable in an "invalid" state. Attempting to extract the value
108from a variable in an invalid state will trigger a check/trap condition, that
109by default will result in process termination.
110
111Here's an example of a buffer calculation using a `CheckedNumeric` type (note:
112the AssignIfValid method will trigger a compile error if the result is ignored).
113
114```cpp
115// Calculate the buffer size and detect if an overflow occurs.
116size_t size;
117if (!CheckAdd(kHeaderSize, CheckMul(count, kItemSize)).AssignIfValid(&size)) {
118 // Handle an overflow error...
119}
120```
121
122### Calculating clamped coordinates (non-sticky saturating arithmetic)
123
124Certain classes of calculations—such as coordinate calculations—require
125well-defined semantics that always produce a valid result on boundary
126conditions. The `ClampedNumeric` template addresses this by providing
127performant, non-sticky saturating arithmetic operations.
128
129Here's an example of using a `ClampedNumeric` to calculate an operation
130insetting a rectangle.
131
132```cpp
133// Use clamped arithmetic since inset calculations might overflow.
134void Rect::Inset(int left, int top, int right, int bottom) {
135 origin_ += Vector2d(left, top);
136 set_width(ClampSub(width(), ClampAdd(left, right)));
137 set_height(ClampSub(height(), ClampAdd(top, bottom)));
138}
139```
140
141*** note
142The `ClampedNumeric` type is not "sticky", which means the saturation is not
143retained across individual operations. As such, one arithmetic operation may
144result in a saturated value, while the next operation may then "desaturate"
145the value. Here's an example:
146
147```cpp
148ClampedNumeric<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
157This header includes a collection of helper `constexpr` templates for safely
158performing 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
199the `strict_cast` template, and can perform valid arithmetic comparisons
200across any range of arithmetic types. `StrictNumeric` is the return type for
201values extracted from a `CheckedNumeric` class instance. The raw numeric value
202is extracted via `static_cast` to the underlying type or any type with
203sufficient 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
212boundary conditions such as overflow, underflow, and invalid conversions.
213The `CheckedNumeric` type implicitly converts from floating point and integer
214data types, and contains overloads for basic arithmetic operations (i.e.: `+`,
215`-`, `*`, `/` for all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers).
216However, *the [variadic template functions
217](#CheckedNumeric_in-checked_math_h-Non_member-helper-functions)
218are the prefered API,* as they remove type ambiguities and help prevent a number
219of common errors. The variadic functions can also be more performant, as they
220eliminate redundant expressions that are unavoidable with the with the operator
221overloads. (Ideally the compiler should optimize those away, but better to avoid
222them in the first place.)
223
224Type promotions are a slightly modified version of the [standard C/C++ numeric
225promotions
226](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)
227with the two differences being that *there is no default promotion to int*
228and *bitwise logical operations always return an unsigned of the wider type.*
229
230### Members
231
232The unary negation, increment, and decrement operators are supported, along
233with 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
246The 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
257The following member functions return a `StrictNumeric`, which is valid for
258comparison and assignment operations, but will trigger a compile failure on
259attempts to assign to a type of insufficient range. The underlying value can
260be extracted by an explicit `static_cast` to the underlying type or any type
261with 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`
274types because they could result in a crash if the type is not in a valid state.
275Patterns like the following should be used instead:
276
277```cpp
278// Either input or padding (or both) may be arbitrary sizes.
279size_t buff_size;
280if (!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
290The following variadic convenience functions, which accept standard arithmetic
291or `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
307The following wrapper functions can be used to avoid the template
308disambiguator 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
314The following general utility methods is are useful for converting from
315arithmetic 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
325and integer data types, saturating on assignment as appropriate. It contains
326overloads for basic arithmetic operations (i.e.: `+`, `-`, `*`, `/` for
327all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers) along with comparison
328operators for arithmetic types of any size. However, *the [variadic template
329functions
330](#ClampedNumeric_in-clamped_math_h-Non_member-helper-functions)
331are the prefered API,* as they remove type ambiguities and help prevent
332a number of common errors. The variadic functions can also be more performant,
333as they eliminate redundant expressions that are unavoidable with the operator
334overloads. (Ideally the compiler should optimize those away, but better to avoid
335them in the first place.)
336
337Type promotions are a slightly modified version of the [standard C/C++ numeric
338promotions
339](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)
340with the two differences being that *there is no default promotion to int*
341and *bitwise logical operations always return an unsigned of the wider type.*
342
343*** aside
344Most arithmetic operations saturate normally, to the numeric limit in the
345direction 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
362The unary negation, increment, and decrement operators are supported, along
363with 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
376The 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
388The following variadic convenience functions, which accept standard arithmetic
389or `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
405The following is a general utility method that is useful for converting
406to a `ClampedNumeric` type:
407
408* `MakeClampedNum()` - Creates a new `ClampedNumeric` from the underlying type
409 of the supplied arithmetic or directly convertible type.
410