"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&=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):

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:

(...)                  ! 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:

(...)                  ! 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:

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

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:
' user pressed <Alternate> and <Right Shift>
ENDIF
' user pressed <Alternate> or <Right Shift>
ENDIF

Multiple                                                 MULTIPLE
Returns smallest multiple of m& ? n% (n%>0):