Skip to main content
© Dave 'Spaz of TLB' Moss

 "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.