 HubPages»
 Technology»
 Computers & Software»
 Computer Science & Programming
QBasic Functions For Floating Point Arithmetic
QBasic supports four numeric data types. These are:
 INTEGER (signed integer stored in 2 consecutive 8bit bytes)
 LONG (signed integer stored in 4 consecutive 8bit bytes)
 SINGLE (single precision floating point number stored in 4 consecutive 8bit bytes)
 DOUBLE (double precision floating point number stored in 8 consecutive 8bit bytes)
QBasic also has the so called "user defined" types which starts with keyword "TYPE" and ends with "END TYPE" (Note: space required between two words). QBasic had carried over from older Basic's (BASICA/GWBASIC) the FIELD statement using which a programmer defines a record area exactly like a TYPE statement except that FIELD statement can only be used in association with a file.
An Example
This code snippet uses TYPE to describe a record in a random file from which last record is read and "ClosingPrice" is extracted:
TYPE PriceRecStruc
DateNumber AS SINGLE
OP AS SINGLE
HP AS SINGLE
LP AS SINGLE
CP AS SINGLE
Volume AS SINGLE
END TYPE
DIM PriceRec AS PriceRecStruc
DIM ClosingPrice AS SINGLE
FileName = "C:\DAT\" + TickerName$ + ".DAT" FileNr = FREEFILE RecLen = LEN(PriceRec) OPEN FileName FOR RANDOM AS FileNr LEN = RecLen
' Calculate Last Record Number NORecs = LOF(FileNr) / RecLen
GET #FileNr, NORecs, PriceRec ClosingPrice = PriceRec.CP CLOSE #FileNr
To achieve same result using FIELD statement we may code this snippet as follows:
DIM ClosingPrice AS SINGLE
FileName = "C:\DAT\" + TickerName$ + ".DAT" FileNr = FREEFILE
RecLen = 24 ' This calculated by hand and hard coded
OPEN FileName FOR RANDOM AS FileNr LEN = RecLen
FIELD FileNr, 4 AS DNS$, _
4 AS OPS$, _
4 AS HPS$, _
4 AS LPS$, _
4 AS CPS$, _
4 AS VolS$
'
' Calculate Last Record Number
NORecs = LOF(FileNr) / RecLen
GET #FileNr, NORecs ' Please note: No variable name to receive
' data, data will be received in above FIELD
' variables
ClosingPrice = CVS(CPS$) ' 4 bytes in CPS$ are transferred to
' ClosingPrice. Both CPS$ and
' ClosingPrice occupy 4 bytes. But only
' ClosingPrice will be treated as single
' precision floating point number
CLOSE #FileNr
CVS and MKS$ Functions
In above code snippet the way to get the 4 bytes from file to be interpreted as single precision number is to use the function CVS. And reverse operation is required to populate FIELD variables. That operation is performed by MKS$. Neither CVS nor MKS$ do any real work. They just transfer 4 bytes from what QBasic thinks as a STRING into what QBasic thinks as a SINGLE.
In some of new BASIC programming systems FIELD statement is not supported but some of these new systems still have CVS and MKS$ functions. Full list of functions needed for inputoutput from a random file is QBasic is given below:
QBasic Functions for use with FIELD statement
MKI$
 CVI
 2 byte integer


MKL$
 CVL
 4 byte integer

MKS$
 CVS
 single precision floating point number

MKD$
 CVD
 double precision floating point number

For Older versions of Basic
Older versions of Microsoft Basic used their proprietory binary format called Microsoft Binary Format (MBF). Those formats were different for floating point values. So if you were processing older random files which had MBF floating point values you need additional two functions as given in following table:
Functions for Older Versions of Floating Point Values
MKSMBF$
 CVSMBF
 older version SINGLE

MKDMBF$
 CVDMBF
 older version DOUBLE

Example: A Program Which Causes Overflow Error
Example: A Program Which Causes Overflow Error
REM FLOAT1.BAS program
REM This program causes Overflow error
CLS
FOR i = 0 TO 1000
x = 2 ^ i
PRINT "x"; x
NEXT i
Two floating point numbers are treated differently by the PRINT statement
Sometimes it prints it as an integer sometimes in scientific notation
REM FLOAT2.BAS program
REM Two floating point numbers are treated
REM differently by the PRINT statement
REM
REM First prints it as an integer and second
REM print in "scientific notation"
CLS
x = 2 ^ 23
PRINT x
x = 2 ^ 24
PRINT x
Output of above program looks like:
8388608
1.677722E+07
Please note that second PRINT has used scientific notation.
Why Two Floating Point Numbers are Treated Differently by PRINT Statement?
We investigate why two floating point numbers are treated differently by QBASIC PRINT statement.
First the program:
REM FLOAT2.BAS program REM This program investigates why two REM floating point numbers are treated REM differently by the PRINT statement REM CLS x = 2 ^ 23 PRINT "We investigate for x="; x: PRINT DIM aString AS STRING aString = MKS$(x) REM REM Now aString has the 4 bytes which REM can be treated as a string. These are REM same 4 bytes which make up the REM internal representation of x REM PRINT "Now we look at individual bytes that make up the internal representation of x" PRINT FOR i = 1 TO 4 PRINT "ith byte's ASCII value, for i="; i, ASC(MID$(aString, i, 1)) NEXT i REM PRINT PRINT "======================================================================" PRINT x = 2 ^ 24 PRINT "We investigate for x="; x: PRINT aString = MKS$(x) REM REM Now aString has the 4 bytes which can treated as a string REM These are same 4 bytes which make up the internal representation of x REM PRINT "Now we look at individual bytes that make up the internal representation of x" PRINT FOR i = 1 TO 4 PRINT "ith byte's ASCII value, for i="; i, ASC(MID$(aString, i, 1)) NEXT i
Now the output generated by above program:
We investigate for x= 8388608
Now we look at individual bytes that make up the internal representation of x
ith byte's ASCII value, for i= 1 0
ith byte's ASCII value, for i= 2 0
ith byte's ASCII value, for i= 3 0
ith byte's ASCII value, for i= 4 75
======================================================================
We investigate for x= 1.677722E+07
Now we look at individual bytes that make up the internal representation of x
ith byte's ASCII value, for i= 1 0
ith byte's ASCII value, for i= 2 0
ith byte's ASCII value, for i= 3 128
ith byte's ASCII value, for i= 4 75
What is happening here? To find out about internal representation of floating point numbers we dig into the help bundled with QBasic interpreter. We position keyboard cursor at the MKS$ function and press function key F1.
Two Digressions On New Computers
Function Keys on New Computers
Many new computers come with default mapping to hardware specific actions mapped onto these function keys. To obtain old behaviour of these keys these computers provide a special key called FN key. Most of these computers allow to restore old behaviours via software or via a BIOS setting. If you are irritated by such computers search manufacturer's documentation on a search page. For example, for my HP laptop I managed to find help on Google search pages which led me to an HP documentation page titled: Disable The Fn + Function Key Combination on Certain Models  HP Customer Care (United States  English).
How to run QBasic on Windows 7?
Also my HP laptop has Windows 7 and to run QBasic I had to download a software called DosBox. I have not managed to configure this software to make it work exactly like QBasic on my older preXP systems. But if you have no option but to use a Window 7 system it is not bad software.
How to Find Help in QBasic
For 99% of your needs you can just position keyboard cursor on a valid QBasic keyword and press function key F1 and QBasic shows the relevant help.
Before we digressed ...
Before we digressed, I was seeking help on MKS$ function. So I positioned keyboard cursor at MKS$ in my program and pressed function key F1. It does not give much information on MKS$ but at bottom of help screen there is reference to two more help screens. One is for FIELD statement and the other is for: "MKSMBF$, MKDMBF$, CVSMBF, CVDMBF". And the second cross reference says following:
MKSMBF$ and MKDMBF$ convert IEEEformat numbers toMicrosoftBinaryformat numeric strings that canbe stored in FIELD statement string variables.CVSMBF and CVDMBF convert those strings back toIEEEformat numbers..MKSMBF$(singleprecisionexpression!)MKDMBF$(doubleprecisionexpression#)CVSMBF (4bytenumericstring)CVDMBF (8bytenumericstring).Function Returns======== ==================================MKSMBF$ A 4byte string containing aMicrosoftBinaryformat number.MKDMBF$ An 8byte string containing aMicrosoftBinaryformat number.CVSMBF A singleprecision number inIEEE format.CVDMBF A doubleprecision number inIEEE format.* These functions are useful for maintainingdata files created with older versions ofBasic.
This gives us the clue that older MicrosoftBinaryformat were before Microsoft QBasic switched to new IEEE format.
After some fumbling I found wikipedia article on IEEE floating point format. It seems that there are more than one IEEE standards for floating point number representation. There is one identified as IEEE 7542008 which is an upgrade of a standard published in 1985 identified as IEEE 7541985. Since QBasic existed before 2008 we should dig deeper into IEEE 7541985. Which is exactly what we are going to do.
Brief Introduction to IEEE7541985 standard
Before we dig deeper into IEEE 7541985 standard we will rewrite our program FLOAT3.BAS to spit out the result in bits format rather than in ASCII values that I have done so far. Also I shall print the result into a file because in DosBox I still do not know how we can copy text for pasting later in a Windows application. This I will do a lot in following and I just wanted to make my life easier, since I would be pasting into a text area in a browser. Feel free to modify the code for your own use for any noncommercial use.
First we show the program FLOAT4.BAS which has these changes:
DECLARE FUNCTION Binary$ (y AS SINGLE)
DECLARE FUNCTION halfByte$ (a AS STRING)
REM FLOAT4.BAS program
REM This program investigates why two
REM floating point numbers are treated
REM differently by the PRINT statement
REM
CLS
OPEN "FLOAT4.TXT" FOR OUTPUT AS #1
x = .15625
PRINT #1, "We investigate for x="; x: PRINT #1,
DIM aString AS STRING
aString = MKS$(x)
REM
REM Now aString has the 4 bytes which
REM can be treated as a string. These are
REM same 4 bytes which make up the
REM internal representation of x
REM
PRINT #1, "Now we look at individual bytes that make"
PRINT #1, "up the internal representation of x"
PRINT #1,
Output$ = ""
FOR i = 1 TO 4
y = ASC(MID$(aString, i, 1))
Output$ = Binary$(y) + " " + Output$
NEXT i
PRINT #1, Output$
CLOSE #1
END
FUNCTION Binary$ (y AS SINGLE)
IF y > 255 OR y < 0 THEN
PRINT "Severe Error: FLOAT4.BAS:Binary$:#1"
PRINT "Program will be terminated"
END
END IF
DIM a AS STRING
IF y < 15! THEN
a = HEX$(y)
Binary$ = "0000 " + halfByte$(a)
ELSE
a = HEX$(y)
Binary$ = halfByte$(LEFT$(a, 1)) + halfByte$(RIGHT$(a, 1))
END IF
END FUNCTION
FUNCTION halfByte$ (a AS STRING)
SELECT CASE a
CASE "0"
halfByte$ = "0000 "
CASE "1"
halfByte$ = "0001 "
CASE "2"
halfByte$ = "0010 "
CASE "3"
halfByte$ = "0011 "
CASE "4"
halfByte$ = "0100 "
CASE "5"
halfByte$ = "0101 "
CASE "6"
halfByte$ = "0110 "
CASE "7"
halfByte$ = "0111 "
CASE "8"
halfByte$ = "1000 "
CASE "9"
halfByte$ = "1001 "
CASE "A"
halfByte$ = "1010 "
CASE "B"
halfByte$ = "1011 "
CASE "C"
halfByte$ = "1100 "
CASE "D"
halfByte$ = "1101 "
CASE "E"
halfByte$ = "1110 "
CASE "F"
halfByte$ = "1111 "
CASE ELSE
PRINT "Severe Error:FLOAT4.BAS:halfByte$:#1"
PRINT "Program will be terminated"
END
END SELECT
END FUNCTION
We test run the program for value in x as 0.15625 because that is the value used in wikipedia article which explains IEEE standard for 32 bit single precision floating point numbers. Click here to go to the relevant section on wikipedia page.
We investigate for x= .15625
Now we look at individual bytes that make
up the internal representation of x
0011 1110 0010 0000 0000 0000 0000 0000
We are happy to see bit pattern as identical to what is shown on wikipedia page. Please notice we had to show bytes in reverse order because that is how QBasic stores them. We will revisit topic of byte order later in another article. For now bear in mind that first bit on left to right scan is bit number 32nd. It is usual to call this as 31st bit because we count from zero. And this is standard way to talk about bits. That is to say, we start with zeroeth bit from right and go on until leftmost bit, the 31st bit. We will use notation like this: [b_{31} ... b_{0}]. For example, for x = .15625, our FLOAT4.BAS output shows the following 32 bits in above output:
[b_{31} ... b_{0}] = 0011 1110 0010 0000 0000 0000 0000 0000
Now we run the program FLOAT4.BAS for values 2^{23} and 2^{24} to see if these in any way warrant for QBasic to treat them differently.
Output for 2^{23}
We investigate for x= 8388608
Now we look at individual bytes that make
up the internal representation of x
0100 1011 0000 0000 0000 0000 0000 0000
The bit b_{31} is "0" here and it is the sign bit which is followed by 8 bits [b_{30} ... b_{23}]: "100 1011 0" which has value of 2^{7} + 2^{4} + 2^{2} + 2^{1} = 128 + 16 + 4 + 2 = 150. This value is the exponent with a bias of 127. By "bias of 127" is meant that we will calculate the value of number represented by [b_{30} ... b_{23}] and subtract 127 to get the power of 2. So value floating point number is obtained by calculating the fractional part (let us call it: F). And then calculating value of power of 2 as: value of [b_{30} ... b_{23}] minus 127. We will call value of bits [b_{30} ... b_{23}] as "e" as is done in the wikipedia article. So "bias of 127" means that power of 2 is calculated as (e  127). And value of floating point number is: (1)^{sign} * 2^{(e127)} * F.
How to calculate fractional part F?
Remaining bits are: [b_{22} ... b_{0}] which are used to represent what is called fractional part. It stands for a binary fraction: 1.b_{22} b_{21} b_{20} ... b_{2} b_{1} b_{0}. Note that the bit "1" before "binary" point is actually not there in storage! It is an assumed bit. The "binary point" is in binary number system what is "decimal point" in decimal number system. By assuming that "1" is there before binary point we are saving a storage area of 1 bit. That is, by using 23 remaining bits we are actually storing a fractional number of 24 bits size if we were to also store the assumed 1 before binary point. For all nonzero fractional numbers we can do that. Because if it is a number smaller that 1 then we will multiply it by "2" repeatedly until the number becomes > 1. If a number > 2, then we can divide it by 2 until it becomes less than 2 and then it will be greater than 1 too. Of course, we will have to adjust exponent or power of 2 accordingly. For fraction which is less than 1 we will subtract 1 from power of 2 every time we multiply by 2. And when converting a fraction > 2 we will add 1 to power of 2 every time we divide by 2. This process of bringing our number in the range greater than equal to 1 and less than 2 is known as normalization.
So for x = 2^{23} the "F" turns out to be: 1.000 0000 0000 0000 0000 0000, which is exactly 1. And exponent (or power of 2) is: 150  127 = 23.
And it is easy to see why 2^{24} will be treated differently. It is the first power of 2 which will require one more bit than there is room for in the fractional part of representation. 224 is in IEEE format will be:
2^{24} = 2^{24} * 1.000 0000 0000 0000 0000 0000 0
except the last "0" is not there because there is no room for that "0" bit. This will give us when "bias 127" is applied an exponent of (24 + 127) = 151. 151 in binary number system is:
100 1011 1
Let us write out the entire 32 bits [b_{31} ... b_{0}]:
0100 1011 1000 0000 0000 0000 0000 0000
We verify this by running FLOAT4.BAS for x = 2^{24}:
Output for 2^{24}
We investigate for x= 1.677722E+07
Now we look at individual bytes that make
up the internal representation of x
0100 1011 1000 0000 0000 0000 0000 0000
Exactly as we thought! Now it only remains to find out how fractional number which is exactly zero is stored in IEEE format. We run the program FLOAT4.BAS for x = 0 and get following result:
We investigate for x= 0 Now we look at individual bytes that make up the internal representation of x 0000 0000 0000 0000 0000 0000 0000 0000
This is consistent with IEEE format. It is written in wikipedia article here that:
The true significand includes 23 fraction bits to the right of the binary point and an implicit leading bit (to the left of the binary point) with value 1 unless the exponent is stored with all zeros.
The word "significand" is new word introduced to term the normalized fraction with a "1" bit as assumed bit before binary point. And when exponent is all zeroes the value of floating point number is 0.0.
The IEEE standard has many other things which I have deliberately left out from this brief introduction to it. Wikipedia is a good starting point to start studying it.
Comments
No comments yet.