The talk covers common misunderstood semantics of integer arithmetic with some simple rules of thumb for minimising mistakes. It covers the arithmetic operators, integer overflow and integral promotion rules.
4. Integer arithmetic
Integers are values, not collections of bits.
Two separate abstraction levels
Bits
Values
Storage
Integer values
with algebraic properties
5. Agenda
• values and storage
bits vs. Values
• Integer arithmetic
bitwise vs. arithmetic operators
• Integer overflow
• Integral promotion rules
unsigned integer promotion
14. Integer arithmetic
• division /, addition +, subtraction -, multiplication *,
modulus %
operates on integers values
• AND &, OR |, NOT ~, XOR ^
operates on independent bits (storage)
• shift left <<, shift right >>
technically arithmetic operators, but want to be bitwise
16. Integer arithmetic
right-shift on integers are defined in terms of mathematical
properties
E1 >> E2
If E1 has an unsigned type or if E1 has a signed type and a
non-negative value, the value of the result is the integral
part of the quotient of E1/2E2.
17. Integer arithmetic
Implementation defined for negative integers
E1 >> E2
If E1 has an unsigned type or if E1 has a signed type and a
non-negative value, the value of the result is the integral
part of the quotient of E1/2E2.
If E1 has a signed type and a negative value, the resulting
value is implementation-defined.
19. Integer arithmetic
if E1 has a signed type and non-negative value, and E1×2E2
is representable in the corresponding unsigned type of the
result type, then that value, converted to the result type,
is the resulting value; otherwise, the behavior is undefined.
left-shift has undefined behavior on negative integers and
implementation defined behavior when shifting out-of-range
E1 << E2
20. Integer arithmetic
left-shift has undefined behavior on negative integers and
implementation defined behavior when shifting out-of-range
E1 << E2
010 . . . . . .
21. Integer arithmetic
shift- and bitwise operators have non-trivial behavior on
signed integers.
Bitwise operators on signed integers depend on the
implementation defined representation
Bits
Signed
Values
Arithmetic operators
Bitwise operators
Implementation
defined mapping
23. Integer arithmetic
• unary - on unsigned integers
unsigned int a = 0;
unsigned int b = -a;
b == 232 - 0
The negative of an unsigned quantity is computed
by subtracting its value from 2n, where n is the
number of bits in the promoted operand.
27. Agenda
• values and storage
bits vs. values
• Integer arithmetic
bitwise vs. arithmetic operators
• Integer overflow
• Integral promotion rules
unsigned integer promotion
28. Integer overflow
• Signed integer overflow has undefined behavior
• Any defined behavior is worse
• You'll get the wrong answer either way
If during the evaluation of an expression, the result is not
mathematically defined or not in the range of representable
values for its type, the behavior is undefined.
29. Integer overflow
• Signed integer overflow has undefined behavior
• Any defined behavior is worse
• You'll get the wrong answer either way
• Unsigned integers don’t have overflow, as they use
modular arithmetic
If during the evaluation of an expression, the result is not
mathematically defined or not in the range of representable
values for its type, the behavior is undefined.
30. Integer overflow
• For symbolic simplification of expressions, they must implement
regular algebra. e.g.
x = (a - b) / c + 10
Given a=-2, c=2 through constant propagation:
x = (-2 - b) / 2 + 10
31. Integer overflow
x = (-2 - b) / 2 + 10
x = -2 / 2 - b / 2 + 10
• For symbolic simplification of expressions, they must implement
regular algebra. e.g.
x = (a - b) / c + 10
Given a=-2, c=2 through constant propagation:
32. Integer overflow
x = (-2 - b) / 2 + 10
x = -2 / 2 - b / 2 + 10
x = -1 - b / 2 + 10
• For symbolic simplification of expressions, they must implement
regular algebra. e.g.
x = (a - b) / c + 10
Given a=-2, c=2 through constant propagation:
33. Integer overflow
x = (-2 - b) / 2 + 10
x = -2 / 2 - b / 2 + 10
x = -1 - b / 2 + 10
x = 9 - b / 2
• For symbolic simplification of expressions, they must implement
regular algebra. e.g.
x = (a - b) / c + 10
Given a=-2, c=2 through constant propagation:
34. • For symbolic simplification of expressions, they must implement
regular algebra. e.g.
x = (a - b) / c + 10
Given a=-2, c=2 through constant propagation:
Integer overflow
x = (-2 - b) / 2 + 10
x = -2 / 2 - b / 2 + 10
x = -1 - b / 2 + 10
x = 9 - b / 2
x = 9 - (b >> 1)
If the hardware supports
shifting negative numbers
35. Integer overflow
• Modular arithmetic
• introduces discontinuities in the value space
• Symbolic simplification of expressions much harder.
Less likely to be optimized
• unsigned integers have these properties
36. A is promoted to unsigned.
(a - b) / 2 is not continuous.
cannot be factored out to: a / 2 - b / 2
Integer overflow
• Same example. If b is unsigned.
x = (a - b) / c + 10
Given a=-2, c=2 through constant propagation:
x = (-2 - b) / 2 + 10
x = -2 / 2 - b / 2 + 10
37. • Same example. If b is unsigned.
x = (a - b) / c + 10
Given a=-2, c=2 through constant propagation:
Actual expression with modulo
semantics (i.e. unsigned)
Integer overflow
x = ((((-2 - b) % 2n) / 2) % 2n) + 10) % 2n
41. • Use signed integers for values that require normal
arithmetic.
• Use unsigned integers for:
• flags (without arithmetic operators)
• IDs (without arithmetic operators)
• enumerations (enum class)
• bits (only used with bitwise operators)
Integer arithmetic
42. Agenda
• values and storage
bits vs. values
• Integer arithmetic
bitwise vs. arithmetic operators
• Integer overflow
• Integral promotion rules
unsigned integer promotion
44. Integral promotion
Integers are promoted to int before applying operators
uint16_t a = …;
uint16_t b = …;
uint16_t c = a + b;
45. Integral promotion
Integers are promoted to int before applying operators
uint16_t a = …;
uint16_t b = …;
uint16_t c = a + b;
uint16_t c = int(a) + int(b);
As if all built-in operators
only take int parameters
46. Integral promotion
• Assuming int ≡ std::int32_t. this causes an overflow
and fail
int32_t c1 = 2’000’000’000;
int32_t c2 = 3;
int32_t c3 = 4;
int64_t result = c1 * c2 / c3;
Intermediate type is int32_t
OVERFLOW!
47. Integral promotion
• ints and wider integral types are promoted to the
highest ranking type
• wider types rank higher
• At same width, unsigned has higher rank
48. Integral promotion
• ints and wider integral types are promoted to the
highest ranking type
• wider types rank higher
• At same width, unsigned has higher rank
Pitfall! This leads to
modular arithmetic
49. Integral promotion
• can cause surprising behavior. e.g.
uint8_t a = 1;
int b = ~a;
std::cout << std::hex << b << "n";
50. • can cause surprising behavior. e.g.
Integral promotion
uint8_t a = 1;
int b = ~a;
std::cout << std::hex << b << "n";
0xfffffffe
Promoted to int
zero-extended
bitwise inverted
52. unsigned promotion
• When wider than int, unsigned types are viral
• can cause surprising behavior if they sneak into
expressions.
53. unsigned promotion
• When wider than int, unsigned types are viral
• can cause surprising behavior if they sneak into
expressions. e.g.
int32_t a = -1;
uint32_t b = 1;
if (a > b) printf("wat");
54. unsigned promotion
int32_t a = -1;
uint32_t b = 1;
if (a > b) printf("wat");
wat
• When wider than int, unsigned types are viral
• can cause surprising behavior if they sneak into
expressions. e.g.
Promoted to unsigned
integer (0xffffffff)
55. Avoid implicit sign conversions
-Wsign-conversion -Werror
Integral promotion
56. • Pick signed/unsigned primarily
based on the arithmetic you want
• Signed integers ↔ arithmetic ops
• Unsigned integers ↔ bitwise ops
• Avoid signed → unsigned promotion
Summary
negative
& positive
only
positive
Normal
arithmetic
int
Modular
arithmetic
unsigned
int
Signed
integer
Unsigned
integer
Arithmetic
operators
Bitwise & shift
operators