“And what do you really do?” said Tiffany.
The thin witch hesitated for a moment, and then:
“We look to… the edges,” said Mistress Weatherwax. “There’re a lot of edges, more than people know. Between life and death, this world and the next, night and day, right and wrong… an’ they need watchin’. We watch ‘em, we guard the sum of things.”
We programmers need to watch the edges, too. Different edges, perhaps, but nevertheless there’re a lot of edges, more than people know.
Back in the ’90s I was administrating a backup system with about 180 client computers. One day it started going wonky. (I don’t remember in what fashion.) I couldn’t figure out why, so naturally I called tech support.
After I described the problem, the support tech said, “What did you change?” I didn’t change anything. “You must have changed something, it was working before and it’s not working now. What have you changed in the last few days?” Certainly the code didn’t change, and it obviously worked, so the fault must lie in the customer. He was very insistent and barely polite.
We did not solve the problem during that phone call.
Two days later an upgrade came out. It seems that when the catalog file grew over 2GB it started having problems. The upgrade fixed that problem. (I don’t remember, but I’ll bet it extended it to 4GB. If you run the numbers I think you’ll find that 2GB is exactly the same as the highest value you can fit into a signed 32-bit integer. What a coincidence!)
What did I change in those few days? Depends on your point of view; all I changed was the amount of data in the catalog, and that was by running the system exactly as I had been. Really I didn’t change anything, but things did change, and the program simply couldn’t keep up.
It’s a perfect illustration of edge conditions that we all should watch. Can the index overflow a word? Will the size of that value really, really work for all eternity, or just for the foreseeable future? Watch out for statements like “Nobody will EVER fill that up!” I heard that in 1982 about 5 megabyte hard drives. Nobody will ever fill that up. Heh. Now you can buy 5 terabytes for a few hundred dollars and carry it around in a couple decent-sized coat pockets. It won’t be long before you can buy it in a single drive for under $100. And nobody with any sense will tell you that you’ll never fill it up.
One very typical programming error is called an off-by-one error, which usually happens when you’re working with zero-based indexes and one-based sizes and lose track of which is which. You can consider this a kind of edge condition; it certainly causes edge-condition-type errors. You can easily copy one too few or one too many items, or simply index past the end of your data. By one. Or never quite reach it. By one.
It’s easy to find those edges in small embedded systems where you typically use as small a variable as you can get away with. I use single-byte words all the time. How often do I use 32-bit integers? Pretty gosh darn seldom. But 32-bit signed integers are the default for the C++ compiler I use for PC programming. It’s actually more efficient to let it default to 32-bit integers than to use bytes, because the native word on the Pentium-class processor is 32 bits. Using anything smaller just makes the system work harder, and it will probably allocate 32 bits anyway because it wants things to align nicely. You probably have to tell it to pack your structs if you want to use them outside the program.
It’s enough to make an 8-bit processor guy cry (for envy) into his special sheep liniment.
What really tends to sneak up to you, though, and bite you on the backside is when you convert or calculate or otherwise change from one word size to another. This happens often when you try to have two different sized systems talking to each other, though of course it can also happen within your small system. It also happens when you get sloppy programmers writing code that uses the minimum needed (but different) size for each variable and then willy-nilly does calculations and copies and indexes back-and-forth without even bothering to cast anything. Of course this guy uses a low warning level when he compiles. And then he leaves the job and you’re left trying to figure out why the code is so buggy. Or rather, why it’s buggy is obvious, but you have to find and kill the bugs.
My current job is all about PC-based flight simulator software exchanging data with simulator hardware that’s based on 8-bit micros. Fortunately most of the actual data are flowing from the hardware to the flight sim software; most of the time the data are going from 10 bits to floating point, and the probability of messing up the scaling is much, much higher than the possibility of losing your high bits.
On the other hand, some of the controls don’t use the full range of the sensors and so must be calibrated. Since the sensor in this case is probably a cheap potentiometer, the wires pick up noise, there’s noise on the power supply, noise introduced by the multiplexers, and noise in the A/D conversion, the value tends to jump around a little. So even the most careful calibration won’t guarantee that the incoming value will never exceed my calibrated limits unless I’m really fanatical or add some padding. Which I don’t really want to do in this case.
What’s that all mean? It means the incoming data will probably exceed my edges, eventually. In this case I simply check the value against the end-points, and if it’s out of range I rail it.
But I do check.
“But there’s magic, too. You’ll pick that up. It don’t take much intelligence, otherwise wizards wouldn’t be able to do it.”
It’s not enough to be a wizard. You need to be a witch, and look to the edges.
Quotes and the reference to Special Sheep Liniment are from The Wee Free Men, a novel of the Discworld by Terry Pratchett.