Bug-free development: Understand the differences between the physical world and the computer world

It is important to understand the differences between the physical world and the computer world, and keep the differences in mind.

A computer, most times, is only simulating the actual world. As a result, if the simulation is not well designed, then a program may get incorrect, inaccurate, and/or inconsistent result. A function once worked one month ago may no longer work today, or its behavior varies from one computer to another. If I said these in front of a programmer, he/she would probably agree. This does not mean he/she would keep this in mind, though. I have seen many cases when the program inaccurately simulated the world, its master’s first instinctive reaction was to check somewhere else, but not their own code.

  • In the physical world there are axioms and laws, while in the computer world there are standards and conventions.
  • In the physical world there are math and physics, while in the computer world there are algorithms.
  • In the physical world numbers can be infinitely large or infinitely small, while in the computer world numbers are represented by limited bits.

When writing programs, the developer have to keep alert of these differences, and when introducing his/her code to others, he/she can easily tell which items are representing the physical world, and which can stand true only on a computer.

To give a quick example here, compare the use of number “2” in the two statements:

circumference = 2 * PI * radius;
p = (short int *)malloc(2 * arraylength);

I believe most developers would have no questions on the first one, and they do not seriously refuse the hard-coded “2”. The reason is obvious: It is math. But to the second line, most developers would shout “no!” because here I am assuming that sizeof(short int) always equals to 2. In the early years, even sizeof(int) was 2 on many PCs, back then many programmers believed that nobody would ever touch this “important” “fundamental” convention, how is the world now?

Consider another piece of code:

for (double f = 0.0; f < 10.0; f += 0.1) {
    // calculations involving f;
}

Nobody can easily predict how many iterations it will loop: 100 or 101? either one is possible. As 0.1 cannot be precisely represented in the computer’s limited binary bits, the loop gradually accumulates the little error on the variable f. If f is involved in calculation, the code should rather be written as:

for (int i = 0; i < 100; i++) {     // to make sure it loops for 100 times
    double f = i / 10.0;     // to avoid accumulated error
    // calculations involving f;
}

I had once seen an instance happened to my colleague. When a real number was written to the company’s data distribution network, the double-precision number was converted into single-precision, and only up to 7 digits of the effective figures can display accurately, beyond that, some became greater and some became less. Before the programmer realized it was a precision problem, he almost believed it was a character-processing bug. This is also an instance that people know the difference between the physical and computer worlds, but do not always remember that.

If the code is heavily condensed with computer stuff, then most times, the bugs are cause by standards, assumptions, range and precision of numbers. Improve the code by removing uncertainties (like the float-point number above) and assumptions (like the sizeof int). If these cannot be completely removed, then try to minimize it.

Many programmers are familiar with this to-upper code:

if (‘a’ <= c && ‘z’ >= c)
    return c – 32;

Here the assumption is the distance between uppercase and lowercase letters in the ASCII table. It is almost true everywhere in the world, yet I still believe the code below is robuster, and more explicit, for it plays no puzzles with the mysterious number “32”:

if (‘a’ <= c && ‘z’ >= c)
    return c + ‘A’ – ‘a’;

To summarize:

  • Know the differences.
  • Keep the differences in mind.
  • Avoid or minimize uncertainties or assumptions.

 


Try it yourself: Run “int((1.0 – 0.3) / 0.1)” in Python, and you will see “6”.

P.S.: Even the physical world plays joke:

1/3 = 0.333…
1/3 * 3 = 1
0.333… * 3 = 0.999…

therefore 1 = 0.999… (this is actually correct, but some people insist that it is mother nature’s bug. :-D)

评论关闭