Task 12: Math Functions  

Contents

The Decimal (BASE10) Numbering System
The Binary (BASE2) Numbering System
Bitwise Math and Boolean Logic
The AND Statement
The OR Statement
The XOR Statement
The NOT Statement
Bitwise Example
Integer Division
Modulus Division ( MOD )
Exponents ( ^ )
The INT Statement
The CINT and CLNG Statements
The CSNG and CDBL Statements
The _ROUND Statement
The FIX Statement
The SQR Statement
The ABS Statement
The SGN Statement
The SIN and COS Statements
The TAN and ATN Statements
Moving Forward
Your Turn - Coming Soon
Command Reference



Before you can get into the math functions built into QB64 you must first understand how a computer counts. In order to understand how a computer counts though you must first understand how a human counts. Why do we have 10 numbers in our counting system ranging from 0 to 9? Why do numbers get longer at 10, 100, 100, and so on? Who invented the human counting system?

It was cave men ... yep ... cave men. One day Ugh was standing at his cave entrance and saw seven woolly mammoths pass by. Hearing his stomach growl he got the hankerin' for a woolly burger. He quickly ran into the cave to tell his friends Unk and Nog what he had seen. Stomping around acting like a woolly mammoth and pointing to the cave entrance his buddies got the general idea that a woolly mammoth was passing by. They were tired though and figured Ugh could handle a single mammoth by himself. It was then that Ugh got a brilliant idea. He envisioned his fingers as individual mammoths! Ugh quickly threw seven fingers into the air, pointed at the cave entrance, and starting stammering about like a mammoth again. Unk thought Ugh might have fleas again and simply ignored him. However Nog saw what Ugh was trying to say, quickly jumped up, motioned Unk to follow and they headed out the cave. The counting system, based on human fingers, 10 of them, emerged! True story.



The Decimal (BASE10) Numbering System


The decimal numbering system is based on 10 digits from 0 to 9. This is also known as the BASE10 numbering system because it's based on 10 individual symbols that represent numbers. In Ugh's time those symbols were fingers however today we use symbols based on Arabic numerals.

BASE10 simply means that any digit to the left of any other digit is worth ten times as much in value. In school you learned these as place holder values with names such as the 1's place, the 10's place, the 100's place, and so on.



Figure 1 - The BASE10 numbering system

Using  Figure 1 as an illustration the decimal number 387,237 can be broken down like this:

(3 x 105) + (8 x 104) + (7 x 103) + (2 x 102) + (3 x 101) + (7 x 100)
 (300000) +  (80000)  +  (7000)  +   (200)   +   (30)    +    (7) = 387,237


In other words each place holder is worth 10 times as much as the place holder to the right of it. Numbering systems are based on something that is familiar or readily available. In the case of humans it's our ten fingers and toes that decided it for us. The Roswell aliens use a BASE6 numbering system because of only having three fingers on each hand (The Grays, not the shape-shifting Reptilians). Computers use a BASE2 numbering system.


The Binary (BASE2) Numbering System


Computers at their core are based on the actions of transistors which are simple electronic switches that can be turned on and off. Because of this computers only know two things, ON or OFF, so a numbering system based on this must be used by them. That numbering system is known as binary, or BASE2, which only contains the two digits 0 and 1. The digit 0 is often referred to as false and the digit 1 referred to as true.

The binary numbering system can be described in exactly the same way as the decimal numbering system with each place holder being based on the number 2 instead of 10. Therefore each place holder is worth 2 times as much as the place holder to the right of it.



Figure 2 - The BASE2 numbering system

The number 183 to a computer in binary is represented as 10110111 and using Figure 2 above as an illustration can be broken down like this:

(1 x 27)+(0 x 26)+(1 x 25)+(1 x 24)+(0 x 23)+(1 x 22)+(1 x 21)+(1 x 20)
  (128) +  (0)   +  (32)  +  (16) +   (0)  +   (4)  +  (2)   +  (1) = 183


Computers use binary numbers to represent the state in which transistors should be placed in. Placing a 1 in video memory for instance tells a transistor to turn on, which in turn lights up a pixel on the monitor. When you create a program it ultimately gets turned into a series of ones and zeros that instruct the millions of transistors to switch on and off. This binary dance is what makes the magic happen on the monitor in front of you.

In computing bit lengths are given names to identify how many bits are contained within a binary number.

Bit         - a single 1 or 0
Nibble      - 4 bits
Byte        - 8 bits or 2 nibbles
Word        - 16 bits, 2 bytes, or 4 nibbles
Double Word - 32 bits, 4 bytes, or 8 nibbles
Quad Word   - 64 bits, 8 bytes, or 16 nibbles



Bitwise Math and Boolean Logic


George Boole was a 19th century mathematician that devised a new form of mathematics based on logic and is often considered the founder of the field of computer science. Today we use his insights into logic, called Boolean logic, to describe the interactions between binary numbers and electronic states. Boolean logic is a perfect fit in computing because it can have only two states, true and false, and therefore only two outcomes, true or false. By equating the two digits of binary to the these states, 0 (zero) for false and 1 (one) for true, we can use George Boole's logic to perform what is known as bitwise math.

Bitwise math consists of operations that act on numbers at the bit level. Bitwise operations are often used for storing useful information and manipulating numbers at the bit level through the use of logic gates. Logic gates are used to sample bits, or inputs, and derive a single output in bit form. There are a number of  logic gate operators available in QB64 that allow the programmer to manipulate numbers at the bit, or binary, level.



The AND Statement


The AND statement is a logical numerical operator that compares two values at the bit level. If two corresponding bits are set to 1, or true, the result of the operation becomes true, anything else will result in false. Logical operators can be described using truth tables. The truth table for the AND logical operator is shown in Figure 3 below.


Figure 3 - AND Truth Table

The only time the AND logical operator will return a value of 1, or true, is if both inputs being tested are 1 or true. The truth table equates to:

0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1

Logical operators also work with zero (false) and non-zero (true) values. A logical operator can be used in the following manner:

IF Number% = 10 AND User$ = "Admin" THEN

The tests on either side of AND will result in either true (-1) or false (0). If both tests result in true then the IF...THEN statement will proceed. Logical operators also work at the bit level on numeric values. Type the following three lines of code in then execute it.

b1% = 255
b2% = 64
PRINT b1% AND b2%


The value of 64 should have been printed, but why? Logical operators look at numeric values in binary. The decimal number 255 when converted to binary is 11111111. The decimal number 64 in binary is 01000000. The bits in each one of these binary numbers is what AND is actually looking at. Figure 4 below shows what this looks like.



Figure 4 - ANDing 255 and 64 Together

The only column with a 1 in both cells is in the 64's place making the answer to ANDing 255 and 64 the binary number of 01000000 or 64 in decimal. Let's AND 237 and 212 together to see the result as seen in Figure 5 below.


Figure 5 - ANDing 237 and 212 Together

In this example the 128's, 64's, and 4's place columns both contain the value of 1 resulting in the binary number 11000100 or 196 in decimal. Many times in source code you'll come across something like this:

Variable% = 255
IF Variable% AND 64 THEN
    ' do some code here
END IF


We already know that the result of ANDing 255 and 64 results in 64. So basically the IF...THEN statement equates to this:

IF 64 THEN

and since 64 is non-zero it will be seen as true and the block of code will be entered. How can code like this be useful? Well get to that in a bit but first there are a few more logical operators to cover.



The OR Statement


The OR logical operator requires two input bits to test as well resulting in the following truth table.


Figure 6 - OR Truth Table

The only time the OR logical operator will return a value 0, or false, is if both inputs being tested are 0 or false. The truth table equates to:

0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1

Just like with AND the OR logical operator can be used for IF...THEN testing:

IF Number% = 10 OR User$ = "Admin" THEN

If either side of OR results in true then the entire statement becomes true.

Let's OR the numbers 237 and 212 together to see the result.



Figure 7 - ORing 237 and 212 Together


The XOR Statement


The XOR, or eXclusive OR, logical operator requires two input bits to test as well resulting in the following truth table.


Figure 8 - XOR Truth Table

The only time the XOR logical operator will return a value of 1, or true, is when only one input is true, not both. The truth table equates to:

0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0

Just like with AND and OR, the XOR logical operator can be used for IF...THEN testing:

IF Number% = 10 XOR User$ = "Admin" THEN

If only one side of XOR results in true the entire statement becomes true.

Let's XOR the numbers 237 and 212 together to see the result.



Figure 9 - XORing 237 and 212 Together


The NOT Statement


The NOT logical operator takes one input and inverts it.


Figure 10 - NOT Truth Table

Many times NOT is used to make a line of code easier to read like so:

CONST FALSE = 0, TRUE = NOT FALSE

PlayerDead% = FALSE ' player is alive
IF NOT PlayerDead% THEN ' is player dead?
    ' do this code
END IF


The NOT logical operator in the line above inverts the value of the variable turning what was a 0 (FALSE) into a -1 (TRUE) and therefore making the IF...THEN statement true.



Bitwise Example


There are many, many variables and conditions to track when writing computer games; is the player alive or dead, did the player grab the key needed to open this door, can the player move in a certain direction, etc. Many of these conditions can be set as true/false flags. Instead of creating a separate variable for each condition the information can be stored in the individual bits of a single variable.

A map can easily be created using bitwise operations to indicate which walls are closed and which are open to move through. Each cell of the map is given the following attributes:



Figure 11 - Setting Up a Map

By adding each binary placeholder with a corresponding wall a number can be created that indicates which walls are turned on. For example, the top left square contains the value of 11 because the North wall has a value of 1 (2^0), the East wall has a value of 2 (2^1) and the West wall has a value of 8 (2^3) that when added together equals 11. Bitwise operations can now be used to test for the existence of a wall in any given cell.

CONST NORTH = 1 ' (2^0)
CONST EAST = 2 '  (2^1)
CONST SOUTH = 4 ' (2^2)
CONST WEST = 8 '  (2^3)

CellWalls% = 11

IF CellWalls% AND NORTH THEN PRINT "North wall is present" ' test the first bit
IF CellWalls% AND EAST THEN PRINT "East wall is present" '   test the second bit
IF CellWalls% AND SOUTH THEN PRINT "South wall is present" ' test the third bit
IF CellWalls% AND WEST THEN PRINT "West wall is present" '   test the fourth bit


The first four bits in CellWalls% are being used as flags to determine if a wall is present or not. The number 11 equates to 1011 in binary. The right-most bit is 1 so there is a wall to the North. The second bit from the right is 1 so there is a wall to the East. The third bit from the right is a 0 so there in no wall to the South. Finally, the fourth bit from the right is 1 so there is a wall to the West. This one variable effectively holds 4 pieces of information for us encoded in its bits.

Here is a demonstration program showing this concept in action. The player can use the arrow keys to move the red circle within the map. The player will not be able to go through walls if they are present. The ESC key allows the player to leave the demonstration. Save the program as BitMaze.BAS when finished typing it in.




Figure 12 - A Bitwise Map

A few things are going on in this example that need explained. First, in lines 73, 77, 81, and 85 the LINE statement is abruptly ended with an underscore ( _ ) character and then the statement resumes on the next line. When a line of code ends in an underscore ( _ ) character that means the rest of the command is continued on the next line. This is used to keep lines of code from scrolling off the right side of the screen. The use of this IDE feature is completely optional but it has been used here so the entire lines of code can be seen in the example above. You'll see more of this use in later tasks as code starts getting more complex.

Secondly, in line 102 you see the DATA statement. The DATA statement is used to store data internally inside of source code. In line 102 each cell of the maze wall values are stored here. In line 69 of the code a READ statement is used to read the DATA stored in line 102. DATA statements are not all that common any longer because it's just as easy to store data in files and read it from there. For the interest of keeping everything within one piece of code a DATA statement was used. Visit the QB64 Wiki for more information on the READ and DATA statements.



Integer Division


Integer division uses the backslash ( \ ) character instead of the foreslash character ( / ) used by standard division. Integer division will only return the whole number, or integer, portion of a result. Integer division is typically used when only integer results are needed such as working with screen coordinates. The following two lines of code highlight the difference between standard division and integer division.

PRINT 10 / 6 ' results in 1.66666 repeating
PRINT 10 \ 6 ' results in 1


Integer division will not round up but instead simply cuts off the fractional portion of a number resulting in the return of the whole number. It's important to remember that integer division will always round the divisor, or second number, up or down accordingly. If you try this:

I% = 10 \ .3 ' division by 0 error!

You will get a division by zero error because .3 was rounded down to zero. The divisor in integer division must always be .5 or greater to avoid a division by zero error.



Modulus Division ( MOD )


In Modulus division (sometimes referred to as remainder division) only the remainder of a division result is returned as an integer. The MOD statement is used to perform modulus division. The following lines of code (copied from the QB64 Wiki) highlight the differences between standard division, integer division, and modulus division.

D! = 100 / 9
I% = 100 \ 9
R% = 100 MOD 9
PRINT "Normal Division:"; D! '    results in 11.11111
PRINT "Integer Division:"; I% '   results in 11
PRINT "Remainder Division:"; R% ' results in 1


The reason modulus division resulted in the value of 1 is because 100 \ 9 = 11 and since 11 * 9 = 99 that leaves a remainder of 1. The MOD statement treats the divisor the same as integer division rounding the divisor up only if it's .5 or greater.

Modulus division is actually used quite often in programming especially in respect to timing in games. Say for instance you have a counter within a loop and you want some code to execute every 30 frames. The MOD statement can help with this.

DO
Count% = Count% + 1 '       increment counter
IF Count% MOD 30 = 0 THEN ' is counter evenly divisible by 30?
    ' execute the code here
END IF
LOOP


Only when Count% is evenly divisible by 30 will there be no remainder:

30 \ 30 = 1 with no remainder
60 \ 30 = 2 with no remainder
90 \ 30 = 3 with no remainder
..
..
etc..



Exponents ( ^ )


Exponent ( ^ ) is used to raise a numeric value to an exponential value, "to the power of". For example:

PRINT 2 ^ 3 ' results in 8 (2 * 2 * 2)

Exponents can also be used to calculate square and cubed roots of numbers like so:

PRINT 144 ^ (1 / 2) ' results in 12 as the square root (12 * 12)
PRINT 27 ^ (1 / 3) '  results in 3 as the cubed root (3 * 3 * 3)



The INT Statement


The INT statement converts a numeric value to an integer by rounding down the value to the next whole number. INT rounds numbers down for both positive and negative values. Therefore:

PRINT INT(2.5) ' result is 2 rounded down

and

PRINT INT(-2.5) ' result is -3 rounded down

are both rounded down. The INT statement comes in handy when you need to convert between data types.

N! = 12.789 '  stored in a single variable type
C% = INT(N!) ' converted to an integer and stored in an integer variable type



The CINT and CLNG Statements


The CINT statement is specifically used to convert decimal numbers to an integer using "Banker's Rounding". This means that numbers with decimal points less than .5 are rounded down, numbers with decimal points higher than .5 are rounded up, and number with .5 as the decimal value are rounded to the nearest even integer value which may be up or down depending on the number. CINT converts numbers to true integers meaning that you must remember to use numbers in the range of 32,767 to -32,768.

PRINT CINT(2.5) '  results in 2 as 2 is the closest even number (round down)
PRINT CINT(3.5) '  results in 4 as 4 is the closest even number (round up)
PRINT CINT(10.6) ' results in 11 (round up)
PRINT CINT(10.4) ' results in 10 (round down)
N! = 1034.5 '      single value in integer range
I% = CINT(N!) '    results in 1034 as it is the closest even number (round down)


The CLNG function is specifically used to convert a decimal number to a long integer. It uses the same Banker's Rounding scheme as CINT. Since CLNG converts to long integers you must remember to use numbers in the range of 2,147,483,647 to -2,147,483,648.



The CSNG and CDBL Statements


The CSNG statement is used to convert a numeric value to the closest single precision value. This function is useful for defining a numeric value as single precision as well.

N# = 895.18741378374
S! = CSNG(N#)
PRINT S! ' results in the single precision value 895.1874


The CDBL statement is used to convert a numeric value to the closest double precision value. This function is mainly used to define any numeric value as double precision.



The _ROUND Statement


The _ROUND statement is used to round a numeric value to the closest even integer, long integer, or _integer64 value. This statement can accept any numeric value to convert and performs the same functions as CINT and CDBL while offering conversion for numbers that exceed long integer in size. Since _ROUND uses the same Banker's Rounding scheme as CINT and CDBL it makes a suitable substitute for both commands.


The FIX Statement


The FIX statement rounds a numerical value to the next whole number closest to zero, in effect truncating, or removing, the fractional portion of a number returning just the integer. This means that the FIX statement rounds down for positive numbers and up for negative numbers.

PRINT FIX(2.5) '  result is 2 printed to the screen
PRINT FIX(-2.5) ' result is -2 printed to the screen



The SQR Statement


The SQR statement returns the square root of any positive numeric value.

N% = 256
PRINT SQR(N%) ' returns 16


Calculating the hypotenuse of a right triangle:

A% = 3 '                        side A
B% = 4 '                        side B
H! = SQR((A% ^ 2) + (B% ^ 2)) ' side C
PRINT "Hypotenuse:"; H! '       value of 5 is printed


Remember that the SQR statement will only work with positive numbers and the accuracy of SQR is determined by the variable type being used to store the result.



The ABS Statement


The ABS statement returns the absolute value of any numeric value in effect turning negative numbers into positive numbers.

N% = -100
PRINT ABS(N%) ' results in 100 printed



The SGN Statement


The SGN statement returns the sign of a numeric value. The value of -1 is returned for values less than zero. The value of 1 is returned for values greater than zero and 0 if the numeric value is zero.

N1% = -10
N2% = 0
N3% = 10
PRINT SGN(N1%), SGN(N2%), SGN(N3%) ' -1  0  1  printed to screen



The SIN and COS Statements


The SIN statement returns the sine of an angle measured in radians. The following is an example that displays the seconds for an analog clock. (Adapted from code by Ted Weissgerber, aka Clippy, found in the QB64 Wiki.)



The COS statement returns the cosine of an angle measured in radians. The following code creates 12 analog hour points that can be used with the clock example above. (Adapted from code by Ted Weissgerber, aka Clippy, found in the QB64 Wiki.)




The TAN and ATN Statements


The TAN statement returns the ratio of sine to cosine, or the tangent value of an angle measured in radians. Here is an example of SIN and TAN working together to create spiraling text. (Adapted from code by John Onyon, aka Unseen Machine, found in the QB64 Wiki.)



The ATN statement returns the arctangent of an angle measured in radians. Here we use ATN to find the angle from the mouse pointer to the center point of the screen. (Adapted from code by Rob, aka Galleon, found in the QB64 Wiki.)




Moving Forward


The new ideas and commands outlined in this task may be a bit difficult to comprehend at first. As you gain programming experience you'll eventually start using these statements as the need arises. Many of the statements introduced here will be covered in more depth in upcoming advanced tasks and their usage will become more clear.


Your Turn


Coming soon







Command Reference


New commands introduced in this task:

AND
OR
XOR
NOT
\ (Integer Division)
MOD
INT()
CINT()
CLNG()
CSNG()
CDBL()
_ROUND()
FIX()
ABS()
SGN()
SIN()
COS()
TAN()
ATN()
READ
DATA


New concepts introduced in this task:

numbering system
transistor
binary
binary numbering system
George Boole
Boolean logic
logic gate
bitwise math
flags
Banker's Rounding
sine
radian
cosine
tangent
arctangent



Commands learned in Task 11:

UCASE$()
LCASE$()
LTRIM$()
RTRIM$()
_TRIM$()
INSTR()
STR$()
DATE$
LEFT$()
RIGHT$()
MID$()
ASC()
STRING$()
SPACE$()
SWAP


Concepts learned in Task 11:

parse



Commands learned in Task 10:

DIM
TYPE
END TYPE
AS
REDIM
_PRESERVE
UBOUND
LBOUND
OPTION BASE


Concepts learned in Task 10:

array
one dimensional array
element
index
two dimensional array
three dimensional array
Dynamic Array
Static Array




Commands learned in Task 9:

LINE INPUT
INKEY$
CHR$()
_KEYDOWN
_KEYHIT
_MOUSEX
_MOUSEY
_MOUSEINPUT
_MOUSEHIDE
_MOUSESHOW
_MOUSEMOVE
_MOUSEBUTTON


Concepts learned in Task 9:

BIOS
ASCII
buffer
CMOS
ROM
ASCII chart



Commands learned in Task 8:

SUB
END SUB
FUNCTION
END FUNCTION
SHARED

Concepts learned in Task 8:

subroutine
function
local variables
global variables
Pythagorean Theorem



Commands learned in Task 7:

SCREEN
LINE
CIRCLE
PAINT
PSET
_RGB32()


Concepts learned in Task 7:

pixels
radian
counter-clockwise
aspect ratio


Commands learned in Task 6:

FOR...NEXT
STEP
CLS
SLEEP
_DELAY
SYSTEM
DO...LOOP (and variations)
WHILE...WEND

Concepts learned in Task 6:

frame rate
controlled loops
conditional loops


Commands learned in Task 5:

TIME$
VAL()
IF...THEN
AND
END
GOTO
SELECT CASE...END SELECT
CASE
TO
IS
CASE ELSE
colon ( : )

Concepts learned in Task 5:

relational operators
functions
nesting
order of operations
Boolean
conditions
indenting
loops
labels


Commands learned in Task 4:

INPUT
DIM
REM or '
CONST

Concepts learned in Task 4:

variables
type declarations
literal strings
concatenation
integers
long integers
single precision
double precision
strings
null strings
reserved words
operators ( +, -, *, / )
declare
constant


Commands learned in Task 3:

PRINT

Concepts learned in Task 3:

execute
statement
expression
literal string
syntax error