Dear DDJ,
The article "Porting Fortran Programs from Minis to PCs," by John Bradberry (September 1990) brought back memories of the most thankless task I have ever undertaken...porting a moderately sized scientific Fortran program to a PC.
I can still recall staring aghast at the countless travesties in style and practice, marveling at the complete lack of structure. Imagine a seemingly endless printout of unindented, line-numbered code with scores of undeclared variables with ingenious names like dxy15, ixxx, ix, ixx, ix1, ixy, nrtsc, iscale1, etc. Every eight or nine vertical inches of code would be interrupted by a block of seven or eight consecutive computed if statements. If subroutines performing multiple tasks are a Do Not, that is still better than a huge program with no subroutines at all ... just gotos and if statements returning control somewhere depending upon the value of a global flag defining the original calling context.
It seemed sad somehow that a very useful, even brilliant, solution to an important problem found expression in such an ungainly program. On the bright side, what was once ugly is now beautiful, and what was once Fortran is now C.
Peter Matsunaga
Aiea, Hawaii
Dear DDJ,
It was a real pleasure to see the article "Porting Fortran Programs from Minis to PCs," by John Bradberry (September 1990). The table of "Do's and Don'ts" was especially interesting to a veteran of some 30 years of wrestling with Fortran mysteries.
But on to the bad news: The listings accompanying the article reveal a commonly made mistake in dealing with type conversions in computed or replacement statements. Consider, for example, the 45th line of the listing, shown on page 80: PI=3.14159265. The assumption usually made is that the information stored in variable PI will be of double precision since PI was declared as type REAL*8.
Example 1 shows that this is not always true. Many compilers, including the Microsoft Fortran 5.0 used here, do not conform to the assumption. The value actually stored in PI in this example turned out to be 3.1459274. This is nowhere near the 15 or 16 digits of precision expected.
C:\>TYPE PIE.FOR
PROGRAM PIE
REAL*8 PI_ONE, PI_TWO
C
PI_ONE=3.14159265 !Common method - without exponent
PI_TWO=3.14159265DO !Correct method - with D exponent
C
WRITE (*, 100) PI_ONE, PI_ONE
100 FORMAT (' This is PI stored without a "D" designator: ',F12.8, 3X,
. Z16, /)
WRITE (*, 110) PI_TWO, PI_TWO
110 FORMAT (' This is PI stored with a "D" designator: ', F12.8, 3X,
. Z16, /)
STOP
END
C:\PIE
This is PI stored without a "D" designator: 3.1459274 400921FB60000000
This is PI stored with a "D" designator: 3.14159265 400921FB53C8D4F1
Stop - Program terminated.
C:\>
In the absence of cues explicitly designating the right side of a statement as double precision, the compilers apparently evaluate the right side in single precision registers. When the time comes to finally replace the left side variable, the right side result, in a single precision register, is transferred to a double precision register and then to the double precision storage location of the variable.
Having found several instances over the years where taking things for granted got me less than I expected, it has become my practice to make statements involving type conversion as "bulletproof" as possible. This means things like explicitly putting in the SNGL and DBLE functions wherever those effects are actually intended to be. In the case of constants, such as 3.1459265, the only way to make constants explicitly double precision is to always include a "D" exponent. For example:
PI=3.14159265D0.
George Zabriskie
Huntsville, Alabama
John responds: Mr. Zabriskie correctly points out a flaw in the initialization of double precision constants in the Fortran listings. Type conversion and mixed-mode arithmetic present such potential problems (in all languages) and were not adequately addressed in my article due to scope and space limitations. The actual use of the constant(s) had no adverse effect on the demonstrated application, but good programming practices and consistency are always worth pointing out.
Dear DDJ,
I just read "Porting C Programs to 386 Protected Mode," by William F. Dudley, Jr. (August 1990) and am writing in response to the statement:
"Now I have a hairy preprocessor macro that computes the size of a 'data element,' which is really the biggest of any of several different structs." (p. 17)
This may not be a complete problem exposition, but it would be unfortunate if such a mechanism were always required. A simple solution is shown in Example 2. This consumes no runtime memory, though if you need the size of a data element, you can now index more efficiently, as in Example 3.
union checksize {
struct struct_1;
struct struct_2;
struct struct_3;
};
#define DATA_ELEMENT_SIZE sizeof(union check.size)
struct struct_1 sl_init; union check_size aray [40], *ptr; int x; ptr = array; for (x=0; x<40; ptr++, x++)ptr->struct_1 = sl_init; John Deurbrouck Mountlake Terrace, Washington
Dear DDJ,
Although I found Jeff Duntemann's column in the August 1990 DDJ interesting, I must take issue with his choice of user interface. Full-screen editors with mouse control have their place, but entering numbers from a bingo card is not that place.
Have you ever seen someone use a 10-key pad such as the one on the right of the PC's keyboard? A skilled operator has two important qualities: He can operate by touch and he can do so very fast. Requiring a 10-key operator to move his attention between the bingo card and the screen is inefficient; to further require him to translate a number into relative screen coordinates (let's see; I'm on 100 and 205 is also circled -- that's five over and two down) is double so.
Back when I did this sort of thing for a living, I could even move my hands between the keypad and the alpha keyboard without looking.
Please take the operator into account when designing user interfaces. Although screen-oriented editors are flashy and fun to program, I believe that Mr. Duntemann's users would appreciate a simpler, enter-the-number interface.
Roger Ivie
Logan, Utah
Dear DDJ,
I too have heard about the unusual programming language Michael Swaine discussed at the end of his January 1990 "Programming Paradigms" column. There were several things about this language he did not mention. This language is particularly well-suited for programming cellular automatons. In addition, AND programs are not structured in a top-down fashion, but form a double helix instead.
Even though there are many millions of different kinds of programs written in AND, current research is trying to take parts of several programs and recombine the pieces into new programs. You would think that with all the programs written in AND there would be one that would fulfill any need. Maybe we conventional programmers can learn a lesson from the AND researchers.
AND programs are susceptible to viruses. Strangely enough, some kinds of bugs in AND programs occasionally manifest themselves as another programming language, the one Swaine discussed in the first part of his column [Lisp].
You would think that with all the AND programs around, there would be a debugger. Maybe there are some technical problems that can't be overcome, like the problem of interpreting the object code. But with only three symbols to consider, this shouldn't be very hard.
At any rate, this is what I know about this unusual programming language. I look forward to hearing more about AND. And please find out the correct name of this language. I'd be ever so interested to know.
Clifford V. Moravetz
Gays Mills, Wisconsin
Dear DDJ,
Regarding Al Stevens's column about the NetWare API (August 1990), I would like to share a method I use for detecting a network. Whenever you call NetWare's GetServerInformation function, the file server name will be zero length if no server can be found. Since I am using C++, I take advantage of default parameters to let you pass the address of a FILE_SERVER_INFO structure (see Example 4) if you so desire, which will then come back containing information on the default server (if any). I think Al will agree this method is a whole bunch simpler.
#include <nit.h> // the NetWare API header
// struct FILE_SERVER_INFO is defined in nit.h
typedef char bool;
bool loggedin (FILE_SERV_INFO * f = NULL){
FILE_SERV_INFO fs, * fp;
fp = (f==NULL)? &fs : f; //use the struct passed, if any
GetServerInformation(sizeof(FILE_SERV_INFO), fp);
return fp->serverName [0];
}
Now that I've stopped to think of what this function does, it's erroneous to call it "loggedin," since what it actually tells you is whether a file server is detected. Guess I'll have to change its name....
Dave Nelson
Jet Propulsion Laboratory
Pasadena, California
Dear DDJ,
I thoroughly enjoyed Larry Spencer's article "Debugging Memory Allocation Errors," (August 1990). The technical content was valuable, and, of course, any similarity between the scenarios painted by him and the life of "real programmers" was totally unintentional!
To add to the humor, there is a small unintentional problem in the mem_Track_free( ) function, which partly defeats the purpose of the function. The simplest correction is to include an additional line, rc = 0, at the very end of the code on the left column on page 179.
To those who did not use Larry's code, I suggest you keep it in a safe place (that is, permanently on top of the highest pile); you know not the hour ...
To those who did, make the above modification and check your program again. Whooooopppppssss!! But the program was perfect yesterday!! OK, OK, OK ... more coffee ... dammmn ... here we go again ...
Michael Kennedy
Dublin, Ireland
Dear DDJ,
In the August 1990 issue, Lawrence Spencer points out some of the problems with dynamic memory allocation in a C/Pascal-like language: "Maybe you freed a pointer that you never allocated" or "Maybe you allocated memory but never freed it" and points out that even veteran programmers are liable to make mistakes of this kind. The results are frequently disastrous, as he points out about the hypothetical program HIGHRISE: "but once in a while it locks up the computer" and "Also, what about the time HIGHRISE forecasted a profit of $17,546,321.97 on a $1,000 investment? . . . Please, let it have been a hardware error!" He then presents some practical solutions for handling this problem in C (I personally have always tended to use something like the solution he presents, in my larger C projects).
But there are other, more general solutions to the problem, albeit outside the context of the article. Use of another language -- a language like Lisp, for instance. Unfortunately, the memory management of Lisp can cause performance problems. Not to mention that the venerable Lisp can be a somewhat alien experience for programmers steeped in the C/Pascal tradition.
Somewhere in between is the new language Clu ("Klu-prime") we provide: This language is a modern version of MIT's CLU language. While memory allocation and deallocation is handled automatically in this language, the performance of this allocation/deallocation is for the most part similar to the programmer making malloc( ) and free( ) calls manually. (This however, is not the primary advantage of using Clu, which supports abstract data types with a very simplified inheritance facility as well as other simple and powerful concepts such as iterators, exception handling, and parametrization.)
In any significantly complex task, dynamic memory allocation/deallocation tends to be enough of a sink of programmer time and effectiveness, and enough source of errors, that I just do not see it remaining under manual control in the programming languages and environments of the future.
Mukesh Prasad
Meta Mind Inc.
East Haven, Connecticutt
Dear DDJ,
I found the July 1990 article "Drawing Character Shapes with Bezier Curves," by Todd King very interesting. Todd is correct in pointing out that the Bezier curve rendering could be improved, especially the parametric equation method. Here is one way to do it.
The equation used for determining the x coordinate on page 48 of the July issue performs fourteen floating-point multiplications, six subtractions, and three additions, plus one to increment the parameter t. Of course, the six subtractions can be reduced to a single subtraction by storing the value of (1 - t) in a variable. Actually, this can be simplified further by realizing that the parameter t starts at 0.0 and is incremented each time by a set stepping value. This implies that a separate variable can be used for (1 - t), which would be initialized to 1.0 and decremented by the stepping value. The six subtractions are replaced by a parameter that is decremented by a constant only once per iteration!
The equation can be simplified by reducing the number of floating-point multiplications, using Horner's rule:
f(t)=[(at + b)t + c]t + d
So if (1 - t)'s are factored out, the first equation becomes:
x(t)=[(x1(1 - t) + 3tx2)(1 - t) + 3t2x3] (1 - t) + t3x4
This equation performs eleven multiplications, three subtractions, and three additions. It is possible to factor ts instead of (1 - t) terms if that's more convenient. In any case, this demonstrates that the first equation is not minimal and can be improved using simple mathematics. More can probably be done.
Todd has a procedure, hull, that shows the convex hull of the four control points. This procedure is incorrect, however. I made a similar mistake in one of my computer graphics projects. For example, if a line was drawn between the first and the last control point, and the second control point was placed on one side of that line, whereas the third control point was placed on the other side of the line, the hull that Todd's procedure draws would not be convex and would not contain the Bezier curve. One of the reasons why folks determine convex hulls is for clipping. If the convex hull of a curve is inside a window, then the whole curve must be inside the window. This means that each of the points on the curve does not need to be checked to see if it is inside the window, and that only the control points need to be tested. Dr. Sedgewick devotes a whole chapter on convex hull determination in his book Algorithms (Addison-Wesley, 1988). It's a very good reference on many topics.
Victor J. Duvanenko
Knightdale, North Carolina