Bruce develops and sells software for TRS-80 and MS-DOS/PC-DOS computers. He can be reached at T.N.T. Software Inc., 34069 Hainesville Road, Round Lake, IL 60073.
Earlier this year, Borland granted to Bob Zale, creator of Turbo Basic, rights to sell future versions of his compiler, which is now being marketed by Spectra Publishing as PowerBasic (PB). PowerBasic's designer has paid primary attention to the needs of the programmer while providing a Basic that is upwardly compatible with Turbo Basic 1.0 and Microsoft's GWBasic.
There are compromises in some areas, but there are very few when it comes to making programming easier and more productive. The result is a worthwhile and unusual version of Basic -- but by no means non-standard.
Compromise of a Sort
Regardless of language, most programmers hate reinventing and rewriting the known. With languages such as C and Pascal, quite a lot of money goes into programmer's toolkits. Still, many common routines fall through the cracks: They're too short and too easy to be worth putting in a toolkit. Worse, some parts of the language itself make the programmer do more work -- with no offsetting gain in clarity.
Consider a simple sort routine. If you were sorting many items, you'd dig out a routine from your favorite toolkit. Alas, the routine might be too general -- requiring that you specify a sort order, a comparison function, data types, and so on. Modifying to sort a half dozen strings might be more trouble than it's worth. So you rewrite a bubble sort for the thousandth time. Sure, it's only a few lines of code, but it can be irritating (especially if you make a mistake in something that elementary).
Or suppose you need a large text array of variable-length strings. I've read quite a few articles about managing such things in C and Pascal, and I've reinvented many of the algorithms in Knuth's books for managing garbage collection. Lately, dynamic string libraries have begun to appear. I suppose they're useful, but I think the need for those libraries begs the question, "Why isn't the requisite functionality built into the language or the standard libraries already?"
QuickBasic and Microsoft's excellent Basic 7.0 still don't permit dynamic strings of more than 64K at one time. Basic 7.0 dodges the question somewhat by allowing the user to have several arrays of as much as 64K at one time. Still, no single array may have more than 64K of dynamic strings.
Fixed-length strings are standard for C and Pascal. There's no question that fixed-length strings are quite useful and even preferable to dynamic strings in many applications. It was an improvement when such strings were added to Basic. However, it's less useful if the length must be specified at compile-time rather than at run time. There are far too many instances (file utilities, sort programs, and so on) where the string length simply cannot be known in advance.
PB doesn't pretend to be an entirely new language, nor does it claim to solve all problems. It's not so much a paradigm shift as an exercise in pragmatism. To the question, "What should Basic be?" Bob Zale has answered: "Whatever Basic programmers want." He didn't implement everything I would like to see, but then, that may be impossible.
Yes, PB includes a sort. The command will sort all or parts of arrays of all data types (the default is plain ASCII), using any collating order you specify, either directly or using a tag array, in ascending or descending order. The sorted arrays may have many dimensions. The options are not mandatory parts of the command, so you can sort a whole array in ascending ASCII order simply as: ARRAY SORT A$( )
That's useful enough, but there's more. You can also scan all or part of an array for the first element that matches a relation you specify (<, >, =, etc.), based (for string arrays) on any collating order you specify -- including case-insensitive scans.
You can also insert or delete an array element with a single command. No more loops or swaps are needed. It seems an enormous understatement to say that all this is just "useful" -- it's been needed for years!
Language Enhancements
These and other enhancements are discussed in the two manuals (User's Manual and Reference Guide) supplied with PB. The documentation is thorough (more than 700 pages), clear, and well indexed and organized, and seems to be free from any obvious typos. I found no errors in the manuals while doing the review, but I'd suggest that later versions of the manuals devote an appendix to the differences between PB and QB. There are appendices detailing the differences between PB and GWBasic, and between Turbo Basic and PB.
Among other things, PB has new data types: Floating-point BCD, fixed-point BCD, 8-byte integer, and 10-byte extended precision floating-point (native coprocessor format numbers). 8-byte integers can have as many as 18 digits, so PRINT USING was revamped.
The expanded string space carries several penalties. Because strings can be spread over more memory, there's more overhead involved. That means string operations tend to be slower in PB than in Microsoft products. But if you need the space, the small penalty is well worth it.
In addition, the FRE(" ") function returns the amount of space remaining in the current string allocation block. Once you've been running the program for a while, there's no easy way for you to tell how much total memory is left for strings, or how much you've used so far. You must calculate the amount of available memory when the program first starts and use that number as the baseline for future calculations.
Those are problems to be noted, but they aren't serious, especially for those of us who find the unlimited string space to be a big advantage.
As with Turbo Basic, PB permits the programmer to specify where segment boundaries lie. From that, you can avoid the problems created by programs too large to fit into a single-user code segment. However, the PB editor and compiler limit source code to 64K bytes. This is less of a problem than before because PB can link with OBJ files and units (resembling Turbo Pascal's units). It can, however, be somewhat of a bother.
Options
Compilation options allow you to generate code for 80286 and 80386 processors; use/emulate a math coprocessor (or ignore the coprocessor and go for maximum speed with a procedural math library); turn on or off various checks; and reduce code size by eliminating support for unnecessary library modules. What you get seems very similar to the options provided in Microsoft's Basic 7.0, though PB's EXE files are usually larger than those of Basic 7.0.
PB permits linking OBJ modules in what seems to me a more natural way than with the Microsoft compilers: PB asks that you include the names of the OBJ modules within the source code of your program. That decreases the length of the command line and the possibility of errors. The compilation options can also be included as meta-commands in the source code. Those enhancements make it much easier to compile PB programs.
There is a drawback, though. PB cannot link with libraries, as QuickBasic can. This can be a big problem if you need many OBJ files.
Benchmarks
While doing this review, I churned out my usual quantitative comparisons. BENCHOLD.BAS (Listing One, page 116) benchmarks features of the language that are common to QuickBasic 3.0 and earlier, while BENCHNEW.BAS (Listing Two , page 119) tests the newer features of the language. The results are shown in Tables 1 and Table 2. Those numbers are not completely irrelevant, but they are of less worth than usual: Power Basic breaks new ground in unexpected ways, and the overall utility of this compiler cannot be judged by noting the relative time it takes to perform a string concatenation or a double precision add.
Table 1: Size of EXEs generated from Listings One and Two
COMPILER PROGRAM SOURCE SIZE EXE SIZE --------------------------------------------- POWER BASIC BENCHNEW 3400 39296 QB 4.5 BENCHNEW 3022 34276* BASIC 7.0 BENCHNEW 3418 24848 POWER BASIC BENCHOLD 8721 46384 QB 4.5 BENCHOLD 7746 37722* BASIC 7.0 BENCHOLD 8720 32684
*Supports fewer tests; size is not strictly comparable, but is provided for reference.
Table 2: Timings were generated using a Tandy 4000 (16-MHz 80386) with an MDA video adapter card, a Casper amber monochrome monitor, and no math coprocessor. For each compiler, all possible stub libraries were used, all error checks were removed, and all speed optimizations employed.
Time, in seconds, per 1,000,000 operations QB 3.0 QB 4.0 BASIC 6 BASIC 7 PB --------------------------------------------------------------------------- Integers: Empty loop 1.21 1.21 1.21 1.20 2.09 assignment: 1.15 1.13 1.13 1.10 1.02 add: .30 .49 .50 .27 .47 subtract: .33 .46 .49 .16 .77 multiply: 1.92 2.45 2.27 2.25 2.77 divide: 3.36 3.43 3.44 3.90 5.41 comparison: 2.58 3.20 2.58 2.53 2.91 Conditional assignment: 4.51 4.77 4.67 2.37 2.31 Conditional assignment*: 4.72 4.88 4.34 1.70 2.47 Long integers: Empty loop ----- 14.45 14.28 12.59 10.49 assignment: ----- 1.62 1.71 1.53 1.67 add: ----- 15.47 15.26 13.23 11.73 subtract: ----- 15.41 15.21 13.57 12.22 multiply: ----- 26.11 25.82 24.34 25.35 divide: ----- 33.76 33.66 31.70 25.68 comparison: ----- 10.81 10.98 10.48 8.57 Single-precision: assignment: 5.71 201.23 20.40 2.20 1.98 add: 23.97 128.83 24.41 42.45 24.66 subtract: 24.80 129.36 25.87 43.72 26.34 multiply: 34.30 -16.19 35.50 52.54 36.02 divide: 35.98 7.48 47.68 64.80 47.02 Error, 100K mult/div: -1.06E-05 -1.19E-07 -1.19E-07 -1.19E-07 -1.96E-5 exponential: 766.80 3061.84 1296.36 1309.90 3373.80 comparison: 19.72 402.00 42.68 42.58 4.55 Double-precision: assignment: 6.26 211.41 23.06 3.49 49.10 add: 45.75 143.28 49.99 68.80 69.48 subtract: 47.19 143.87 51.52 70.39 69.43 multiply: 83.13 199.49 91.66 109.60 86.13 divide: 84.22 226.45 121.82 142.57 130.08 Error, 100K mult/div: -4.82E-13 -2.22E-16 2.22E-16 2.22E-16 -4.22E-15 exponential: 2491.80 6377.23 3046.33 3240.62 3326.56 comparison: 20.37 412.89 46.30 46.37 100.68 Strings: assignment: 77.09 78.50 79.04 77.72 72.56 MID$ operation: 20.78 24.61 25.20 24.11 81.93 concatenation: 1657.81 954.84 1661.15 1662.48 875.16 Print 1K 70-byte strings**: 26.47 10.00 10.05 9.55 9.61 Fixed string assignment: ----- 52.38 51.92 51.68 101.86 Fixed string MID$ operation: ----- 23.44 24.12 23.74 147.13 Fixed string concatenation: ----- 24.61 24.78 21.01 170.85 Pr 1K 70-b static strings**: ----- 10.16 10.10 9.65 9.60
- One of the ways should be appreciatively faster if short-circuit optimization is being done ** To the screen. Monochrome display, MDA card
Note: the negative and small times for single-precision multiply and divide for QB 4.0 mean that they took less time to execute than a simple assignment. Perhaps there are unnecessary checking or conversion operations going on during the simple assignment. Considering the big slow-down from QB 3.0, that seems likely.
Operation speed QuickBasic 3.0 = 100.0 (Lower numbers are better)
QB 4.0 BASIC 6 BASIC 7 PB ------------------------------------------------------------------ Empty integer loop 100.0 100.0 99.2 172.7 Integer assignment: 98.3 98.3 95.7 88.7 Integer add: 163.3 166.7 90.0 156.7 Integer subtract: 139.4 148.5 48.5 233.3 Integer multiply: 127.6 118.2 117.2 144.3 Integer divide: 102.1 102.4 116.1 161.0 String assignment: 101.8 102.5 100.8 94.1 String MID$ operation: 118.4 121.3 116.0 394.3 String concatenation: 57.6 100.2 100.3 113.1 Single-precision assignment: 3524.2 357.3 38.5 34.7 Single-precision add: 537.5 101.8 177.1 102.9 Single-precision subtract: 521.6 104.3 176.3 106.2 Single-precision multiply: -47.2 103.5 153.2 105.0 Single-precision divide: 7.4 47.6 180.1 130.7 Single-precision exponential: 399.3 169.1 170.8 440.0 Double-precision assignment: 3377.2 368.4 55.8 784.3 Double-precision add: 313.2 109.3 150.4 151.9 Double-precision subtract: 304.9 109.2 149.2 147.1 Double-precision multiply: 240.0 110.3 131.8 103.6 Double-precision divide: 268.9 144.6 169.3 154.5 Double-precision exponential: 255.9 122.3 130.1 133.5 Integer comparison: 124.0 100.0 98.1 112.8 Single-precision comparison: 402.0 216.4 215.9 73.8 Double-precision comparison: 2027.0 227.3 227.6 494.3 Conditional int assignment: 105.8 103.5 52.5 51.2 Conditional assignment*: 103.4 91.9 36.0 52.3 Print 1K 70-byte strings**: 37.8 38.0 36.1 36.3 Unweighted average performance: 500.4 136.4 119.7 176.8 Operation speed, Basic 6.0 = 100.0 QB 4.0 BASIC 7 PB ------------------------------------------------------------ Empty long integer loop 101.2 88.2 73.5 Long integer assignment: 94.7 89.5 97.7 Long integer add: 101.4 86.7 76.9 Long integer subtract: 101.3 89.2 80.3 Long integer multiply: 101.1 94.3 98.2 Long integer divide: 100.3 94.2 76.3 Fixed string assignment: 100.9 99.5 196.2 Fixed string MID$ operation: 97.2 98.4 610.0 Fixed string concatenation: 99.3 84.8 689.5 Long integer comparison: 98.5 95.4 78.1 Print 1K 70-byte static strings**: 100.6 95.5 95.0 Unweighted performance average: 99.7 92.3 197.4
True compilation is faster in PB. With the Microsoft products, any request to create an EXE will result in a call to the command line compiler and the separate linker. PB compiles in memory.
If you ask to create an EXE in memory, the Microsoft products are faster; they're not really compiling but converting the program to a tokenized form. The difference shows when you run the result. PB programs run at EXE speed, even in the environment. PB compiles slower because it's really compiling, but that doesn't mean it's a slug. Where QuickBasic might take 2 seconds to tokenize and start running a program, PB might take 10. The QB program might take 20 seconds to run in the environment, where the PB version might run in 5.
So, who's faster? It depends. If you often need to stop a program and rerun it with minor changes, QB might be best. But if the program is complex enough, PB might give a faster turnaround. The more complex the program, the bigger the advantage for PB. PB permits conditional compilation, too. This is a feature I'd hoped Microsoft would include in Basic 7.0.
Environmental Issues
PB's editor is much better than Microsoft's. The user interface (in my opinion) hurts the Microsoft products. The QB editor will not get out of the way, and constantly presents dialog boxes telling you of syntax errors while you're doing ordinary editing from line to line.
Another problem is that the QB editor has no line continuation character. Long lines must be viewed in parts or split into sections. This can be a nuisance because the QB editor insists on inserting spaces at nearly every opportunity. Even modestly complex lines can easily become too wide and can't be split. Additionally, the QB editor will not permit the programmer to mark a block of code and write it to a disk file, or to read a block from a disk file into the current program.
By default, the QB editor saves new programs in a special format. The format varies from release to release, and none are backwardly or upwardly compatible.
In contrast, PB works with and saves plain ASCII files, alters nothing you type, permits line continuation characters, and allows block read and write. It's just plain easier and faster to type and edit code with PB.
To be fair, I find the QB help system easier to use than PB's. That may be a factor for beginners. And, QB's instant debugging is probably better for programmers not familiar with Basic. Again, there are definite trade-offs. PB's editor is perhaps better suited to programmers with some experience in Basic.
PB's debugging options are improved over Turbo Basic's, but Microsoft still has a clearly better approach. PB's debugger is competent but clumsier and somewhat more difficult to use.
PB's debugger doesn't permit you to use function calls, but only to change the value of a variable or an expression that evaluates to a variable. Further, PB replaces PRINT USING during debugging with a series of powerful but arcane formatting commands. The result is flexible and more capable than what Microsoft delivers, but I found it confusing. The commands are reminiscent of Fortran with some C conventions. Fortunately, for most debugging sessions you'll never need to worry about formatted print for variables.
Trade-offs
Another point that PB has in its favor is the physical size of the compiler environment. PB can be run easily on a floppy disk system. Many beginners don't have hard disks. Even if you installed every PB file on your hard disk, the total would come to just about 700K -- much less than QuickBasic 4.5, and far less than the 6 to 14 Mbytes required for Basic 7.0.
Part of the reason for the size difference: PB lacks support for OS/2 and doesn't have the ISAM file capability of Basic 7.0. Nor does PB come with the equivalent of CodeView. But I have yet to find a use for CodeView when doing Basic programming.
PB doesn't support record or file locking, either. Of all the possible disadvantages I can imagine, this is probably the most serious. While OS/2 is very much a minor market, LANs are everywhere on the increase. So far, though, fewer than 20 percent of MS-DOS machines are part of a network. Of that minority, many do not need shared file access through a user-written program; a large number share files only through networked word processors or the like. Realistically, PB's potential market should not be damaged greatly by the lack of support for shared data files. It is, however, something that I recommend be added soon.
Putting It to Work
To demonstrate some of PowerBasic's new features, consider the simple mailing list program in Listing Three (page 120). The program certainly doesn't exercise all the capabilities of PB, but it should be enough to give you a taste of them.
Listing Three doesn't contain a search function, holds all data in memory, and has primitive editing capabilities (you can delete a record and insert a new one, and that's it). You can, however, hold more than 3100 records in memory on a 640K machine (473,176 bytes available for string data). The sort is fast, record insertion is nearly instantaneous, and it would be trivial to add features for finding a record, editing by field, sorting by additional fields, retaining a backup data file, and so on. In fact, most things you might want to do to this program are easy.
Conclusion
PB's new features are a strong selling point. Its emphasis on genuinely useful enhancements, the nice editor, the faster execution in the environment, and the new data types make it an essential product for anyone seriously interested in Basic.
Neither PB nor Microsoft's Basic 7.0 is the "best" Basic compiler in every area. PB needs support for libraries, file and record locking, larger source files, user-defined types, and a better debugger. Microsoft could benefit from PB's editor and environment, new functions, larger string space, link and meta-command options, conditional compilation, and added data types.
I intend to convert several of my programs from QB and Basic 7.0 to PB. In fact, I began the process soon after getting my review copy of PB. PB's advantages in particular areas make the conversion attractive. Other programs will remain in the Microsoft dialect. I suspect that most Basic programmers will have the same experience.
If you're serious about Basic programming, you really ought to consider having both PB and one of the Microsoft products. And Turbo Basic 1.x owners will find the $59 upgrade is money well spent.
Product Information
PowerBasic Spectra Publishing 1030-D E. Duane Sunnyvale, CA 94086 408-730-9291 Price: $129 Upgrade for Turbo Basic users $59 Requirements: IBM PC/compatible MS-DOS 2.0 or higher 640K RAM, one floppy drive
_THE POWER IN POWER BASIC_ by Bruce Tonkin
[LISTING ONE]<a name="0189_0010">
DEFINT A-Z
DIM t!(28)
OPEN "basoldpb.tim" FOR OUTPUT AS #1
'time for a raw integer loop, executed 1,000,000 times.
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
NEXT j
NEXT i
t!(0) = TIMER - t!
'time for a integer assignment loop, executed 1,000,000 times.
y = 5: z = -5
PRINT "Int =";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x = y
x = z
NEXT j
NEXT i
t!(1) = (TIMER - t! - t!(0)) / 2
'time for 1,000,000 integer adds
y = 5: z = -5
PRINT "+";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x = x + y
x = x + z
NEXT j
NEXT i
t!(2) = (TIMER - t! - t!(0)) / 2 - t!(1)
'time for 1,000,000 integer subtracts
PRINT "-";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x = x - y
x = x - z
NEXT j
NEXT i
t!(3) = (TIMER - t! - t!(0)) / 2 - t!(1)
'time for 1,000,000 integer multiplies
k = 7
PRINT "*";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x = k * j
NEXT j
NEXT i
t!(4) = TIMER - t! - t!(1)
'time for 1,000,000 integer divides
PRINT "\"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x = i \ j
NEXT j
NEXT i
t!(5) = TIMER - t! - t!(1)
'time for 100,000 string assignments
PRINT "String=";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 100
x$ = "abcdefghijklmnopqrstuvwxyz"
x$ = "zyxwvutsrqponmlkjihgfedcba"
NEXT j
NEXT i
t!(6) = (TIMER - t! - t!(0) / 10) / 2
'time for 100,000 string MID$ operations
x$ = "abcdefghijklmnopqrstuvwxyz"
k = 17
PRINT "Mid$";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 100
MID$(x$, k, 1) = "d"
NEXT j
NEXT i
t!(7) = TIMER - t! - t!(0) / 10
'time for 10,000 string concatenations
x$ = ""
PRINT "+"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 10000
x$ = x$ + "a"
NEXT i
t!(8) = TIMER - t! - t!(6) / 10 - t!(0) / 100
'time for a single-precision assignment loop, executed 1,000,000 times.
y! = 5!: z! = -5!
PRINT "Single=";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x! = y!
x! = z!
NEXT j
NEXT i
t!(9) = (TIMER - t! - t!(0)) / 2
'time for 1,000,000 single-precision adds
PRINT "+";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x! = x! + y!
x! = x! + z!
NEXT j
NEXT i
t!(10) = (TIMER - t! - t!(0)) / 2 - t!(9)
'time for 1,000,000 single-precision subtracts
x! = 0!
PRINT "-";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x! = x! - y!
x! = x! - z!
NEXT j
NEXT i
t!(11) = (TIMER - t! - t!(0)) / 2 - t!(9)
'time for 100,000 single-precision multiplies
x! = 1!
PRINT "*";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 100
x! = x! * 1.00001
NEXT j
NEXT i
t!(12) = TIMER - t! - t!(0) / 10 - t!(9) / 10
'time for 100,000 single-precision divides
PRINT "/";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 100
x! = x! / 1.00001
NEXT j
NEXT i
t!(13) = TIMER - t! - t!(0) / 10 - t!(9) / 10
'error in single-precision multiply/divide
t!(14) = x! - 1!
'time for 10,000 single-precision exponentiations
x! = 100!
PRINT "^"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 10000
x! = x! ^ .999999
NEXT i
t!(15) = TIMER - t! - t!(0) / 100 - t!(9) / 100
'time for a double-precision assignment loop, executed 1,000,000 times.
y# = 5.5#: z# = -5.5#
PRINT "Double=";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x# = y#
x# = z#
NEXT j
NEXT i
t!(16) = (TIMER - t! - t!(0)) / 2
'time for 1,000,000 double-precision adds
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
PRINT "+";
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x# = x# + y#
NEXT j
NEXT i
t!(17) = TIMER - t! - t!(16) - t!(0)
'time for 1,000,000 double-precision subtracts
x# = 0#
PRINT "-";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x# = x# - y#
NEXT j
NEXT i
t!(18) = TIMER - t! - t!(16) - t!(0)
'time for 100,000 double-precision multiplies
x# = 1#
PRINT "*";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 100
x# = x# * 1.00001#
NEXT j
NEXT i
t!(19) = (TIMER - t! - t!(0) / 10) - t!(16) / 10
'time for 100,000 double-precision divides
PRINT "/";
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 100
x# = x# / 1.00001#
NEXT j
NEXT i
t!(20) = (TIMER - t! - t!(0) / 10) - t!(16) / 10
'error in double-precision multiply/divide
t!(21) = x# - 1#
'time for 10,000 double-precision exponentiations
x# = 100#
PRINT "^"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 10000
x# = x# ^ .999999#
NEXT i
t!(22) = (TIMER - t! - t!(0) / 100) - t!(16) / 100
'following are logical comparisons and operators
'time for 1,000,000 integer comparisons
x = 0
PRINT "Int compar"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
IF i < x THEN x = 1
NEXT j
NEXT i
t!(23) = TIMER - t! - t!(0)
'time for 1,000,000 single-precision comparisons
x! = 5!: y! = 3.333
PRINT "Single compar"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
IF x! < y! THEN x = 1
NEXT j
NEXT i
t!(24) = TIMER - t! - t!(0)
'time for 1,000,000 double-precision comparisons
x# = 5#: y# = 3.333#
PRINT "Double compar"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
IF x# < y# THEN x = 1
NEXT j
NEXT i
t!(25) = TIMER - t! - t!(0)
'is there short-circuit expression evaluation?
'integer loop, 1,000,000 times
PRINT "Short"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
IF i < 0 AND j < 10 THEN x = 1
NEXT j
NEXT i
t!(26) = TIMER - t! - t!(0)
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
IF j < 10 AND i < 0 THEN x = 1
NEXT j
NEXT i
t!(27) = TIMER - t! - t!(0)
'Note: if the two times are appreciably different, some optimization has been
'done. The first time should be shorter than the second.
'screen output: print 1,000 70-byte strings
x$ = STRING$(70, 66)
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
PRINT x$
NEXT i
t!(28) = TIMER - t! - t!(0) / 1000
'print results of benchmark
PRINT #1, "Empty integer loop, 1,000,000 iterations:"; TAB(46); t!(0)
PRINT #1, "1,000,000 integer assignments:"; TAB(46); t!(1)
PRINT #1, "1,000,000 integer additions:"; TAB(46); t!(2)
PRINT #1, "1,000,000 integer subtractions:"; TAB(46); t!(3)
PRINT #1, "1,000,000 integer multiplications:"; TAB(46); t!(4)
PRINT #1, "1,000,000 integer divisions:"; TAB(46); t!(5)
PRINT #1, "100,000 string assignments:"; TAB(46); t!(6)
PRINT #1, "100,000 string MID$ operations:"; TAB(46); t!(7)
PRINT #1, "10,000 string concatenations:"; TAB(46); t!(8)
PRINT #1, "1,000,000 single-precision assignments:"; TAB(46); t!(9)
PRINT #1, "1,000,000 single-precision additions:"; TAB(46); t!(10)
PRINT #1, "1,000,000 single-precision subtractions:"; TAB(46); t!(11)
PRINT #1, "100,000 single-precision multiplications:"; TAB(46); t!(12)
PRINT #1, "100,000 single-precision divisions:"; TAB(46); t!(13)
PRINT #1, "Error in 100,000 single-precision mult/div:"; TAB(46); t!(14)
PRINT #1, "10,000 single-precision exponentiations:"; TAB(46); t!(15)
PRINT #1, "1,000,000 double-precision assignments:"; TAB(46); t!(16)
PRINT #1, "1,000,000 double-precision additions:"; TAB(46); t!(17)
PRINT #1, "1,000,000 double-precision subtractions:"; TAB(46); t!(18)
PRINT #1, "100,000 double-precision multiplications:"; TAB(46); t!(19)
PRINT #1, "100,000 double-precision divisions:"; TAB(46); t!(20)
PRINT #1, "Error in 100,000 double-precision mult/div:"; TAB(46); t!(21)
PRINT #1, "10,000 double-precision exponentiations:"; TAB(46); t!(22)
PRINT #1, "1,000,000 integer comparisons:"; TAB(46); t!(23)
PRINT #1, "1,000,000 single-precision comparisons:"; TAB(46); t!(24)
PRINT #1, "1,000,000 double-precision comparisons:"; TAB(46); t!(25)
PRINT #1, "1,000,000 conditional integer assignments:"; TAB(46); t!(26)
PRINT #1, "1,000,000 conditional assignments (reversed):"; TAB(46); t!(27)
PRINT #1, "Print 1,000 70-byte strings to the screen:"; TAB(46); t!(28)
END
<a name="0189_0011"><a name="0189_0011">
<a name="0189_0012">
[LISTING TWO]<a name="0189_0012">
DEFLNG A-Z
DIM t!(28)
OPEN "basnewpb.tim" FOR OUTPUT AS #1
MAP x1$$ * 1
MAP x26$$ * 26
MAP x70$$ * 70
MAP x10000$$ * 10000
'time for a raw long integer loop, executed 1,000,000 times.
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
NEXT j
NEXT i
t!(0) = TIMER - t!
'time for 1,000,000 long integer assignments.
y = 5&: z = -5&: PRINT "Long int="
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x = y
x = z
NEXT j
NEXT i
t!(1) = (TIMER - t! - t!(0)) / 2
'time for 1,000,000 long integer adds
PRINT "+"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x = x + y
NEXT j
NEXT i
t!(2) = TIMER - t! - t!(1)
'time for 1,000,000 long integer subtracts
x = 0
PRINT "-"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x = x - y
NEXT j
NEXT i
t!(3) = TIMER - t! - t!(1)
'time for 1,000,000 long integer multiplies
PRINT "*"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x = i * j
NEXT j
NEXT i
t!(4) = TIMER - t! - t!(1)
'time for 1,000,000 long integer divides
PRINT "/"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
x = i \ j
NEXT j
NEXT i
t!(5) = TIMER - t! - t!(1)
'time for 100,000 fixed string assignments
PRINT "Fixed string="
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 100
x26$$ = "abcdefghijklmnopqrstuvwxyz"
x26$$ = "zyxwvutrsqponmlkjihgfedcba"
NEXT j
NEXT i
t!(6) = (TIMER - t! - t!(0) / 10) / 2
'time for 100,000 fixed string MID$ operations
k = 17
PRINT "Mid$"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 100
MID$(x26$$, k, 1) = "d"
NEXT j
NEXT i
t!(7) = TIMER - t! - t!(0) / 10
'time for 10,000 fixed string "concatenations"
x$ = ""
PRINT "+"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 10000
MID$(x10000$$, i, 1) = "a"
NEXT i
t!(8) = TIMER - t! - t!(0) / 100
'following are logical comparisons and operators
'time for 1,000,000 long integer comparisons
x = 5&: y = -5&
PRINT "Long int compar"
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
FOR j = 1 TO 1000
IF i < y THEN x = 1
NEXT j
NEXT i
t!(23) = TIMER - t! - t!(0)
'screen output: print 1,000 70-byte fixed strings
x70$$ = STRING$(70, 66)
t! = TIMER: WHILE t! = TIMER: WEND: t! = TIMER
FOR i = 1 TO 1000
PRINT x70$$
NEXT i
t!(28) = TIMER - t! - t!(0) / 1000
'print results of benchmark
PRINT #1, "Raw long integer loop, 1,000,000 iterations:"; TAB(45); t!(0)
PRINT #1, "1,000,000 long integer assignments:"; TAB(45); t!(1)
PRINT #1, "1,000,000 long integer additions:"; TAB(45); t!(2)
PRINT #1, "1,000,000 long integer subtractions:"; TAB(45); t!(3)
PRINT #1, "1,000,000 long integer multiplications:"; TAB(45); t!(4)
PRINT #1, "1,000,000 long integer divisions:"; TAB(45); t!(5)
PRINT #1, "100,000 fixed string assignments:"; TAB(45); t!(6)
PRINT #1, "100,000 fixed string MID$ operations:"; TAB(45); t!(7)
PRINT #1, "10,000 fixed string concatenations:"; TAB(45); t!(8)
PRINT #1, "1,000,000 long integer comparisons:"; TAB(45); t!(23)
PRINT #1, "Print 1,000 70-byte strings to the screen:"; TAB(45); t!(28)
END
<a name="0189_0013"><a name="0189_0013">
<a name="0189_0014">
[LISTING THREE]<a name="0189_0014">
defint a-z
cls
recordlength=152
print"Sample in-memory mailing list program demo for Dr. Dobbs Magazine."
open"r",1,"sample.dat",recordlength
field #1,recordlength as w$
'set up a flex string variable to hold the data fields
map rec$$*recordlength,10 as dte$$,25 as last$$,1 as middle$$,_
20 as first$$,25 as address1$$,25 as address2$$,25 as city$$,_
2 as state$$,9 as zip$$,10 as phone$$
total=lof(1)\recordlength 'number of records in the sample file
y=(fre(-1)-32000)\recordlength 'number of records that will fit in memory
if total>y then print"Not enough room for this file.":close:end
dim records$(1:y) 'ordinary string array will hold the records.
print"Please wait:reading the data file.";
propellor$="|/-\":where=pos(9)
for i=1 to total
locate csrlin,where:print mid$(propellor$,1+i mod 4,1);
get 1,i
records$(i)=w$
next i
close 1
current=1
while 1
cls
print total;" total records of a maximum of";y
print"Current record is #";current
locate 4,1
rec$$=records$(current)
print "Date entered: ";dte$$;" Phone:";phone$$
print "Name: ";rtrim$(first$$);" ";middle$$;" ";last$$
print "Address: ";address1$$
print "Address: ";address2$$
print "City, state, zip: ";rtrim$(city$$);" ";state$$;" ";zip$$
locate 12,1
print"E=enter new record D=delete current record Q=quit without saving"
print"S=sort records J=jump to record X=save and exit"
while not instat:wend:cmd$=inkey$
locate 15,1:cmd$=ucase$(cmd$)
select case cmd$
case "E"
gosub entry
case "D"
gosub deleterec
case "Q"
gosub quit
case "S"
gosub sortrecs
case "J"
gosub jump
case "X"
gosub exitprog
end select
wend
entry:
'insert new record at current position
dte$$=date$
line input"First name: ";first$$
line input"Middle initial: ";middle$$
line input"Last name: ";last$$
line input"Address #1: ";address1$$
line input"Address #2: ";address2$$
line input"City: ";city$$
line input"State: ";state$$
line input"Zip: ";zip$$
w$=rec$$
array insert records$(current),w$
total=total+1
return
deleterec:
'get rid of current record
array delete records$(current)
total=total-1:if total<current then current=total
if current<1 then current=1
return
quit:
end
sortrecs:
'sort by last name.
array sort records$(1) for total, from 11 to 35
return
jump:
input"Jump to record: ";current
if current>total then current=total
if current<1 then current=1
return
exitprog:
print"Saving data file. ";
open"o",1,"sample.dat"
close 1
open"r",1,"sample.dat",recordlength
field #1,recordlength as w$
where=pos(9)
for i=1 to total
lset w$=records$(i)
put 1,i
locate csrlin,where:print mid$(propellor$,1+i mod 4,1);
next i
close:print
end