"If you're right 90% of the time, why quibble about the
remaining 3%?"
YOUR SECOND GFA BASIC 3.XX MANUAL
- or -
HOW I LEARNED TO STOP WORRYING AND LOVE GFA-BASIC
PART 7
CHAPTER SIX - OPERATORS AND NUMERICAL FUNCTIONS
by Han Kempen
Integer-commands
Although I did not thoroughly test this, it seems a one-line
combination of integer-commands is faster than using the commands
on separate lines:
x&=ADD(y&,2) ! two separate lines
x&=SUB(x&,2)
'
x&=SUB(ADD(y&,2),2) ! same result, but faster
In a compiled program it's the other way around, but there the
difference is too small to bother me.
\
The operator '\' (backslash) is identical to 'DIV':
a=b\c ! same as: a=b DIV c
By the way, integer-division is not as useful as the other
integer-operations, because the result is an integer. Of course
it is, but an integer-division can easily result in a large
rounding error.
PRED and SUCC
PRED(i) is faster than i-1 and SUCC(i) is faster than i+1. Both
functions can also be used with strings. Note the clever use
(ahem) of SUCC in the following line from the Procedure Clock,
where the rightmost digit of clock$ (the seconds) is increased
with one:
MID$(clock$,8)=SUCC(RIGHT$(clock$))
MOD
If you use a counter in a loop in order to do something every
time the counter reaches a multiple of 10, you could do this as
follows:
IF counter MOD 10=0 ! multiple of 10?
(...) ! yes, do something
ENDIF
You could also use :
IF MOD(counter,10)=0
(...)
ENDIF
BCLR
In GFA-Basic version 2.x you used AND to clear a bit:
x|=x| AND &X11111011 ! clear bit 2 of this byte
Remember, the first (rightmost) bit is bit 0, so a byte contains
bit 0-7 from right to left. In GFA-Basic 3.0 you clear a bit
simply with:
x|=BCLR(x|,2) ! clear bit 2 of this byte
If you want to clear more than one bit, you should use the AND-
method:
x|=x| AND &X11110000 ! clear bit 0-3 of this byte
In this case you use a so-called bit-mask to clear certain bits.
You can use AND also as a function:
y|=AND(x|,&X11110000) ! even faster than previous example
A well-known example of this method is the fastest way to adjust
a screen-address to a multiple of 256 (in this case by using a
mask to clear the first 16 bits):
adr%=AND(ADD(adr%,255),&HFFFFFF00)
BSET
In GFA-Basic 2.x you needed OR to set a bit, but in GFA-Basic
3.x you use BSET:
x|=BSET(x|,1)
You still need the OR-method if you want to set more than one
bit fast:
x|=x| OR &X1010 ! set bit 1 and 3
The mask &X1010 is used to set bits 1 and 3. Compare this with
the AND-method, where 0 is used to clear a bit, while here 1 is
used to set a bit. You can use OR not only as an operator, but
also as a function:
y|=OR(x|,&X1010)
BCHG
Instead of BCHG you should use the XOR-method if you want to
change more than one bit (with a mask):
x|=x| XOR &X1110 ! change bit 1-3
XOR can also be used as a function.
BTST
With BTST you can test if a certain bit in a variable is set:
IF BTST(x|,3)
(...) ! yes, bit 3 of x| was set
ENDIF
If you want to test more bits, you could repeatedly use BTST,
but again it's easier to use a mask:
IF AND(x|,mask|)=mask|
(...) ! all set bits in mask| also found
in x|
ENDIF
You can also test if at least one bit of a mask is set in a
variable:
IF AND(x|,mask|)
(...) ! at least one hit in x|
ENDIF
LOG
Logarithms with a base other than 10 or e are computed as
follows:
LOG(x)/LOG(base)
SINQ and COSQ
If you are going to convert SIN (or COS) into SINQ (COSQ),
you'll have to change from radials to degrees. For example, you
would convert SIN(x) to SINQ(DEG(x)). Although SINQ is about five
times faster than SIN, this extra computation makes the
difference a little less spectacular. If you are lucky, the
variable already is in degrees, so you can use SINQ immediately.
SINQ and COSQ should not be used if you need very accurate
results, only if you plot the result and are not going to use it
for further computations. Examine the difference between the use
of SIN/COS and SINQ/COSQ to see if the less accurate result is
acceptable. In High resolution try something like this:
' SIN: accurate, but slow
fac#=2*PI/639
DRAW 0,200
FOR x=0 TO 639
y#=SIN(x*fac#)
DRAW TO x,200+y#*200
NEXT x
~INP(2)
CLS
'
' SINQ with conversion from radials to degrees
DRAW 0,200
FOR x=0 TO 639
y#=SINQ(DEG(x*fac#))
DRAW TO x,200+y#*200
NEXT x
~INP(2)
CLS
'
' SINQ with degrees: the fastest
fac#=360/639
DRAW 0,200
FOR x=0 TO 639
y#=SINQ(x*fac#)
DRAW TO x,200+y#*200
NEXT x
~INP(2)
EQV-bug
EQV doesn't work properly in an interpreted program (GFA 3.07).
EQV(0,-1) should be 0, but equals -65536. The bits 16-31 are
always set and EQV seems to look at the bits 0-15 only. If you
stick to words and bytes EQV works fine, but you can't use
integers:
PRINT EQV(&HFFFF,&HFFFF) ! OK: bit 0-15 identical
PRINT EQV(&H1FFFF,&H1FFFF) ! bit 16 gives trouble
Of course, you will probably use '=' if you want to check if all
bits of two variables are equal. But you might need EQV to
determine how well two variables fit: the more bits are set by
EQV, the better the fit. In that case you should clear bit 16-31:
fit|=AND(EQV(object|,mask|),&HFFFF)
You could use the same method to check if all bits in two words
are different:
fit&=AND(EQV(x&,y&),&HFFFF) ! fit&=0 if all bits are
different
In a compiled program (and in GFA 3.5E) EQV seems to work all
right.
CARD and SWAP
If a 4-byte integer (postfix %) consists of two words, you can
extract these (interpreted as positive numbers!) with:
low.word&=CARD(x%)
high.word&=CARD(SWAP(x%)) ! swap low/high word first
Unfortunately, there is no analogue function to swap the low and
high byte of a word. That would be useful if you want to convert
a word into/from Intel-format (MS-DOS). Rotating 8 bits should do
the trick:
x%=x&
intel.word&=CARD(ROR&(x%,8)) ! swap low/high byte
Please note that x% is an integer-variable, although you are
manipulating a word. Try x& and watch what happens.
You don't need the above swap-method to extract the low and high
byte of a word, you can use:
low.byte|=BYTE(x&)
high.byte|=BYTE{V:x&}
ABS-bug
In a compiled program, ABS returns weird results if you use a
word-array. With integer-arrays and floating point arrays ABS
behaves as it should. And in an interpreted program you get
correct results with word-arrays as well.
ROUND
Rounding in (any) Basic always gives me a severe headache. Try
this:
x#=9.35
y#=(28+9.5/3)/(3+1/3)
PRINT x#'ROUND(x#,1) ! 9.35 becomes 9.4
PRINT y#'ROUND(y#,1) ! 9.35 becomes 9.3
Just another result of the internal binary representation of
floating point numbers...
Pre-rounding to one extra decimal place once seemed a good idea
to me:
PRINT ROUND(ROUND(x#,2),1) ! 9.4
PRINT ROUND(ROUND(y#,2),1) ! 9.4
Unfortunately this method only creates new problems:
z#=9.345
PRINT z#'ROUND(ROUND(z#,2),1)
We certainly don't want 9.4 here, do we?
The only solution to the ROUNDing-problem I know is to add a
small number to the variable before ROUNDing. If a computation
results in an answer with two significant decimals, you could add
a small number like 1.0E-06 (the sixth decimal place doesn't
matter):
PRINT y#'ROUND(y#+1.0E-06,1)
But you'll have to determine yourself how small the number must
be. It may be as small as 1.0E-13, but anything smaller is
ignored. That's because GFA uses 14 digits internally (and shows
not more than 13). I think. If you know a better solution, please
let me know. Excuse me, I'll have to take an aspirin now.
Procedures (CHAPTER.06)
Array_compress ARR_COMP
Remove duplicate elements from sorted word-array (dimension of
array is changed!):
@array_compress(sorted.array&())
Array_frequency ARR_FREQ
Count the frequency of positive numbers in a word-array:
@array_frequency(numbers&(),frequency&())
The word-array must not contain negative numbers! The frequency-
array is created in the Procedure. The Procedure uses the
Function Array_max to determine the dimension of the frequency-
array. You can examine the frequency-table as follows:
FOR i=0 TO DIM?(frequency&())-1
PRINT i;" occurred "frequency&(i)" times"
NEXT i
Correlation CORRELAT
Calculate the correlation between two word-arrays:
@correlation(0,100,n1&(),n2&(),cor#,sign!,a#,b#)
Elements 0 through 100 are compared. The correlation cor# is
returned (only valid if significant: sign!=TRUE). The parameters
of the regression-line (a# and b#) are also returned: y = a# *
x + b#
Pie_diagram PIE_DIAG
A pie-diagram of the positive numbers in a word-array (max. 24
elements) is drawn on the High or Medium screen:
@pie_diagram(numbers&())
The numbers are converted to percentages and are also printed.
Functions (CHAPTER.06)
Log LOG
Returns logarithm with any base (other than e or 10):
PRINT @log(12,4.25) ! base 12
The following are all goniometric functions: \GONIO\*
Arccot Sinh Cosh Tanh Coth Arsinh Arcosh Artanh Arcoth
PRINT @sinh(0.5)
Array_freq ARR_FRQ
Returns the frequency of a number in a word-array:
PRINT @array_freq(15,numbers&()) ! how many times does 15
occur?
Array_freq_limit ARR_FRQL
Returns the frequency of all numbers above/below (and including)
a certain limit in a word-array:
PRINT @array_freq_limit(TRUE,10,numbers&())
The example counts how many times numbers ? 10 occur in the
array. If the flag is FALSE, numbers ? 10 are counted.
Array_max ARR_MAX
Returns the largest number in a word-array:
PRINT @array_max(numbers&())
Array_mean ARR_MEAN
Returns the mean of a word-array:
PRINT @array_mean(TRUE,numbers&())
If the flag is TRUE, zeroes are used in the calculation. If the
flag is FALSE, zeroes are ignored.
Array_min ARR_MIN
Returns the smallest number in a word-array:
PRINT @array_min(numbers&())
Array_sum ARR_SUM
Returns the sum of all numbers in a word-array:
PRINT @array_sum(numbers&())
Binomial_chance BIN_CHNC
Returns binomial chance, what else:
PRINT @binomial_chance(100,0.5,50)
In this case the chance is calculated that you'll get exactly 50
times heads if you toss a coin 100 times. The Function Faculty is
used.
Denominator DENOMNTR
Returns the largest common denominator of two numbers:
PRINT @denominator(1200,55)
Uses the same algorithm as Euclid once used.
Faculty FACULTY
Returns the faculty of a number:
PRINT @faculty(100)
You'll get an overflow-warning if you try 450.
Mask_test MASKTEST
Returns TRUE if a bit-mask fits on a word-variable. If the flag
is TRUE all mask-bits must be found, otherwise at least one mask-
bit must be found:
IF @mask_test(TRUE,BIOS(11,-1),&X1001)
' user pressed <Alternate> and <Right Shift>
ENDIF
IF @mask_test(FALSE,BIOS(11,-1),&X1001)
' user pressed <Alternate> or <Right Shift>
ENDIF
Multiple MULTIPLE
Returns smallest multiple of m& ? n% (n%>0):
adr%=@multiple(adr%,256)
In this case a screen-address is converted to a multiple of 256.
But this is not the quickest way to do that (page 6-2).
Swap_word (page 6-5) SWAPWORD
Convert a word from/to Intel-format (you know, MS-DOS, IBM,
etc.):
intel.word&=@swap_word(atari.word&)
Disclaimer
The text of the articles is identical to the originals like they appeared
in old ST NEWS issues. Please take into consideration that the author(s)
was (were) a lot younger and less responsible back then. So bad jokes,
bad English, youthful arrogance, insults, bravura, over-crediting and
tastelessness should be taken with at least a grain of salt. Any contact
and/or payment information, as well as deadlines/release dates of any
kind should be regarded as outdated. Due to the fact that these pages are
not actually contained in an Atari executable here, references to scroll
texts, featured demo screens and hidden articles may also be irrelevant.