language | title | date |
---|---|---|
en-us |
How two's complement works |
2018-01-10 |
Two's complement is widely used to represent integer numbers in computers. We'll explain how it works with examples and justify why it works by mapping it onto modular arithmetic.
Digital computers use storage units with fixed length in bits: a byte
(8 bits), a word (32 or 64 bits) etc. Each bit is denoted by 0 or 1 composing a
bit string. It doesn't require any additional structure to represent natural
numbers,
Given a set of bit string elements with
When necessary throughout this post, we'll denote fixed-length numbers in parentheses along with a base (or an overlined base if referring to a complement) in subscript.
For the following examples, we'll consider bit strings with
For integers in
For integers in
- Write the corresponding opposite's bit string
- Invert (negate) its bits
- Add
$1$
For example, for
$$ \begin{align*} (10100100)_2 + 1 = (10100101)2 \ \implies -91 = (10100101){\overline{2}} \end{align*} $$
As a result,
Applying those steps to all integers in
$$ \begin{align*} -128 &= (10000000){\overline{2}} \ -127 &= (10000001){\overline{2}} \ & \dots \ -109 &= (10010011){\overline{2}} \ & \dots \ -91 &= (10100101){\overline{2}} \ & \dots \ -3 &= (11111101){\overline{2}} \ -2 &= (11111110){\overline{2}} \ -1 &= (11111111)_{\overline{2}} \end{align*} $$
A nice feature of two's complement is that we can easily tell whether it is
representing a negative number by looking at its first bit: if starting with 1,
then negative (just like a sign bit). And an unlikable feature is that
the minimum negative integer, here
Now let's investigate further what that operation is all about. In the example
for
We don't have enough bits to write an unsigned integer
OK, and what's special about the calculation
Such operation is called two's complement, and can be used to reserve the
upper half of the
At this point you may note: if
Back to the previous example, then why should we calculate the tricky
The concept explained above can be generalized for an
A quick example for base
$$ \begin{array}{l|l|l|l|l} \begin{array}{c} 0 = (00){\overline{5}} \ 1 = (01){\overline{5}} \ 2 = (02){\overline{5}} \ 3 = (03){\overline{5}} \ 4 = (04){\overline{5}} \end{array} & \begin{array}{c} 5 = (10){\overline{5}} \ 6 = (11){\overline{5}} \ 7 = (12){\overline{5}} \ 8 = (13){\overline{5}} \ 9 = (14){\overline{5}} \end{array} & \begin{array}{c} 10 = (20){\overline{5}} \ 11 = (21){\overline{5}} \ 12 = (22){\overline{5}} \ -12 = (23){\overline{5}} \ -11 = (24){\overline{5}} \end{array} & \begin{array}{c} -10 = (30){\overline{5}} \ -9 = (31){\overline{5}} \ -8 = (32){\overline{5}} \ -7 = (33){\overline{5}} \ -6 = (34){\overline{5}} \end{array} & \begin{array}{c} -5 = (40){\overline{5}} \ -4 = (41){\overline{5}} \ -3 = (42){\overline{5}} \ -2 = (43){\overline{5}} \ -1 = (44)_{\overline{5}} \end{array} \end{array} $$
You can verify it yourself by hand as an exercise or just play with the
implementation in the next section. Notice that due to an odd number of
elements,
<form lang="en" style="font-family: monospace;">
<p>
base =
<input
oninput="calc();"
id="b"
type="number"
step="1"
style="width: 4em;"
value="2"
min="2"
max="16"
/>
<small>min=2, max=16</small><br />
n digits =
<input
oninput="calc();"
id="n"
type="number"
step="1"
style="width: 4em;"
value="8"
min="1"
/>
<small>min=1, max=<span id="max_n"></span></small><br />
decimal =
<input
oninput="calc();"
id="decimal"
type="number"
step="1"
style="width: 8em;"
value="-91"
/>
<small
>min=<span id="min_decimal"></span>, max=<span id="max_decimal"></span
></small>
</p>
<p>
radix complement = (<span id="radix-complement-string"></span>)<sub
id="b-sub"
style="text-decoration: overline;"
></sub>
</p>
</form>
<script>
// radixComplementString returns a string with n digits representing decimal
// in a radix complement according to base b.
// Ranges:
// b in [2, 16]
// n in [1, ceil(log_b(2^32))]
// decimal in
// [-(b^n)/2, (b^n)/2] , if b^n is odd,
// [-(b^n)/2, (b^n)/2 - 1], if b^n is even.
function radixComplementString(b, n, decimal) {
// Apply radix complement formula
if (decimal < 0) {
decimal = Math.pow(b, n) + decimal;
}
// Get digit string and pad zeros
return decimal.toString(b).padStart(n, "0");
}
function calc() {
// Get values.
var b = document.getElementById("b").valueAsNumber;
var n = document.getElementById("n").valueAsNumber;
var decimal = document.getElementById("decimal").valueAsNumber;
// Validate base.
if (isNaN(b) || b == 1) {
return; // User hasn't finished typing yet; do nothing.
} else if (b < 2) {
b = 2;
document.getElementById("b").value = "2";
} else if (b > 16) {
b = 16;
document.getElementById("b").value = "16";
}
document.getElementById("b-sub").innerHTML = b.toString();
// Validate n digits.
var max_n = Math.ceil(Math.log(Math.pow(2, 32)) / Math.log(b));
if (isNaN(n)) {
n = 1;
} else if (n < 1) {
n = 1;
document.getElementById("n").value = "1";
} else if (n > max_n) {
n = max_n;
document.getElementById("n").value = max_n.toString();
}
document.getElementById("max_n").innerHTML = max_n.toString();
// Define min and max decimal values.
var N = Math.pow(b, n);
var min_decimal = -Math.floor(N / 2);
var max_decimal = Math.floor(N / 2);
if (N % 2 == 0) {
max_decimal -= 1;
}
document.getElementById("min_decimal").innerHTML = min_decimal.toString();
document.getElementById("max_decimal").innerHTML = max_decimal.toString();
// Validate decimal.
if (isNaN(decimal)) {
decimal = 0;
} else if (decimal < min_decimal) {
decimal = min_decimal;
document.getElementById("decimal").value = min_decimal.toString();
} else if (decimal > max_decimal) {
decimal = max_decimal;
document.getElementById("decimal").value = max_decimal.toString();
}
// Show radix complement string.
document.getElementById("radix-complement-string").innerHTML =
radixComplementString(b, n, decimal);
}
calc();
</script>
Two's complement, or radix complement in general, works because the elements and
operation involved form a cyclic abelian group directly related to the
group of integers modulo m under addition,
denoted as
Let
Given
Regarding the
Then