Eleven hours on one question, and the answer turns out to be six different agreements about the same thirty two wires.
Which values should these patterns mean?
Four billion patterns in thirty two wires, and no rule anywhere in the hardware about what any of them denotes.
/* 01-six.c */ #include <stdio.h> #include <string.h> int main(void) { unsigned pattern = 0x52732E31; int i; float f; char c[5] = { 0 }; memcpy(&i, &pattern, 4); memcpy(&f, &pattern, 4); memcpy(c, &pattern, 4); printf("unsigned %u\n", pattern); printf("two's complement %d\n", i); printf("Q16.16 fixed %.5f\n", pattern / 65536.0); printf("IEEE 754 float %g\n", f); printf("four characters %c%c%c%c\n", c[3], c[2], c[1], c[0]); return 0; }
| scheme | holds | spacing | fails when |
|---|---|---|---|
| unsigned | 0 to 4,294,967,295 | even, always 1 | it wraps, in silence |
| two's complement | −2,147,483,648 to 2,147,483,647 | even, always 1 | it wraps, and one negative has no positive twin |
| fixed point | a range you choose, exactly | even, you choose it | it wraps, and the range is small |
| IEEE 754 | 10−45 to 1038, approximately | grows with the value | it rounds, everywhere, always |
| ASCII | 128 characters | not a number | the world is not English |
| UTF-8 | every character there is | not a number | length stops having one answer |
/* 02-laws.c */ #include <stdio.h> int main(void) { double A = 1e16, B = -1e16, C = 1.0; double x = 0.1, y = 0.2, z = 0.3; double nan = 0.0 / 0.0; printf("a + b == b + a %s\n", A + B == B + A ? "yes" : "NO"); printf("a * b == b * a %s\n", x * y == y * x ? "yes" : "NO"); printf("(a + b) + c == a + (b + c) %s\n", (A+B)+C == A+(B+C) ? "yes" : "NO"); printf("(x * y) * z == x * (y * z) %s\n", (x*y)*z == x*(y*z) ? "yes" : "NO"); printf("x * (y + z) == x*y + x*z %s\n", 0.1*(0.1+0.7) == 0.1*0.1 + 0.1*0.7 ? "yes" : "NO"); printf("(a + c) - c == a %s\n", (C + A) - A == C ? "yes" : "NO"); printf("a + 1 > a %s\n", A + 1 > A ? "yes" : "NO"); printf("a == a %s\n", nan == nan ? "yes" : "NO"); printf("0.1 + 0.2 == 0.3 %s\n", x + y == z ? "yes" : "NO"); return 0; }
Commutativity holds because the hardware adds two numbers without caring which arrived first. The rounding happens once, to one result.
Associativity fails because moving the brackets changes which intermediate value gets rounded, and the size of a rounding depends on the magnitude it lands on.
/* 04-both.c · the same four laws, both types */ #include <stdio.h> int main(void) { float a = 1e8f, b = -1e8f, c = 1.0f, p = 0.1f, q = 0.2f, r = 0.3f; double A = 1e8, B = -1e8, C = 1.0, P = 0.1, Q = 0.2, R = 0.3; printf("(a+b)+c == a+(b+c) %-7s %s\n", (a+b)+c == a+(b+c) ? "yes" : "NO", (A+B)+C == A+(B+C) ? "yes" : "NO"); printf("(p*q)*r == p*(q*r) %-7s %s\n", (p*q)*r == p*(q*r) ? "yes" : "NO", (P*Q)*R == P*(Q*R) ? "yes" : "NO"); printf("p*(q+r) == p*q + p*r %-7s %s\n", p*(q+r) == p*q + p*r ? "yes" : "NO", P*(Q+R) == P*Q + P*R ? "yes" : "NO"); printf("p + q == r %-7s %s\n", p + q == r ? "yes" : "NO", P + Q == R ? "yes" : "NO"); return 0; }
/* 03-types.c */ #include <stdio.h> #include <limits.h> #include <float.h> int main(void) { printf("char %5zu %-21d %d\n", sizeof(char), CHAR_MIN, CHAR_MAX); printf("short %5zu %-21d %d\n", sizeof(short), SHRT_MIN, SHRT_MAX); printf("int %5zu %-21d %d\n", sizeof(int), INT_MIN, INT_MAX); printf("long %5zu %-21ld %ld\n", sizeof(long), LONG_MIN, LONG_MAX); printf("float %5zu %-21.4g %.4g\n", sizeof(float), FLT_MIN, FLT_MAX); printf("double %5zu %-21.4g %.4g\n", sizeof(double), DBL_MIN, DBL_MAX); printf("\ngap next to 1.0: float %g, double %g\n", FLT_EPSILON, DBL_EPSILON); return 0; }
limits.h and float.h will tell you.| failure | what was actually happening | status |
|---|---|---|
| 0.1 + 0.2 | one tenth repeats forever in binary, so it is stored as the nearest float and two roundings do not cancel | closed, Class 6 |
| Ariane 5 | a 64 bit float written into a 16 bit integer with no range check, because the check was omitted on the strength of Ariane 4 flight data | closed, Class 10 |
| the wallpaper crash | 271 into eight bits keeps the low bits and gives 15 | mechanism known |
| 2038 | a 32 bit signed counter reaching 2,147,483,647 and crossing to the negative half of the circle | mechanism known |
Two are finished. For the other two you can now explain exactly what happened, and still not why nothing said a word.
When 271 goes into eight bits, something knows. A carry came off the end and had nowhere to go.
That fact exists. It is a wire. Nobody in this arc could look at it, because we have never built anything that has wires.
Two's complement was chosen so that a machine which counts forwards would produce correct subtraction. We never built the thing that counts.
The float layout puts the exponent above the mantissa so that comparing two floats can be done by comparing them as integers. We never built a comparator.
Every one of these representations was designed around a circuit,
and we have not built a single circuit.
Not what represents 6. You already know that. What physical thing takes the pattern for 5 and produces the pattern for 6.
Bring nothing. We start with two switches and a lamp.