6. OPERATORS and NUMERICAL FUNCTIONS 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 (page 3-8), 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 (page 6-3) 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 (page 6-3) 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 and ENDIF IF @mask_test(FALSE,BIOS(11,-1),&X1001) ' user pressed or 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&)