Numeric Data Types

When starting out with a new programming language, one of the first things you should learn is the language’s data types. Virtually every program manipulates data, and to correctly manipulate that data you must thoroughly understand the available data types. Data type errors rank second to syntax errors but they are a lot more troublesome. The compiler can catch syntax errors and some data type errors, but most data type errors occur when the program is running, and often only when using certain types of data. These kind of intermittent errors are difficult to find and difficult to fix. Knowing the kind, size and limits of the data types will help keep these kinds of errors to a minimum.

FreeBasic has all the standard numeric data types that you would expect for a Basic compiler, as well as pointers which you usually only find in lower-level languages such as C. The following table lists all the numeric data types that FreeBasic supports. In the list below, you will notice that Integer and Long are grouped together. This is because a Long is just an alias for Integer. They are exactly the same data type.

Numeric Data Type Size Limits
Byte 8-bit signed, 1 byte -128 to 127
Double 64-bit, floating point, 8 bytes -2.2E-308 to +1.7E+308
Integer (Long) 32-bit, signed, 4 bytes -2,147,483,648 to 2,147,483,647
LongInt 64-bit, signed, 8 bytes -9,223,372,036,854 775 808 to 9,223 372,036,854,775,807
Short 16-bit, signed, 2 bytes -32,768 to 32,767
Single 32-bit, floating point, 4 bytes 1.1 E-38 to 3.43 E+38
UByte 8-bit, unsigned, 1 byte 0 to 255
UInteger 32-bit, unsigned , 4 bytes 0 to 4,294,967,295
ULongInt 64-bit, unsigned, 8 bytes 0 to 18,446,744,073,709,551,615
Ushort 16-bit, unsigned, 2 bytes 0 to 65365
Pointer 32-bit, memory address, 4 bytes Must be initialized at runtime

Signed Versus Unsigned Data Types

Signed data types, as the name implies, can be negative, zero or positive. Unsigned types can only be zero or positive, which give them a greater positive range than their signed counterparts. If your data will never be negative, using the unsigned data types will allow you to store larger numbers in the same size variable.

The Floating Point Data Type

The floating point data types, Single and Double are able to store numbers with decimal digits. Keep in mind that floating-point numbers are subject to rounding errors, which can accumulate over long calculations. You should carry more than the number of decimal digits than you need to ensure the greatest accuracy.

Pointer Data Types

Pointer data types are unlike the other data types in that they store a memory address and not data. Since pointers are handled differently than regular data types, a separate tutorial has been devoted to the subject and will not be examined in this tutorial.

Numeric Variables

The numeric data types define what numbers you can work with in your program, but you must create variables to actually hold the numbers. A variable is a name composed of letters, numbers or the underscore character such as MyInteger, or MyInteger2. There are rules for variable naming though, that you must keep in mind.

  • Variable names must start with a letter or the underscore character. It is not recommended however, to use the underscore character, as this is generally used to indicate a system variable, and it is best to avoid it in case there may be conflicts with existing system variable names.
  • Most punctuation marks have a special meaning in FreeBasic and cannot be used in a variable name. While the period can be used, it is also used as a deference operator in Types, and so should not be used to avoid confusion or potential problems with future versions of the compiler.
  • Numbers can be used in a variable name, but cannot be the first character. MyVar1 is legal, but 1MyVar will generate a compiler error.

The Dim Statement

In order to use a variable, you must instruct the compiler to create one using the Dim statement. When the compiler encounters a Dim statement, it will set aside some memory for a variable of a particular data type. For example, consider the following code snippet.

Dim myInteger as Integer

This command instructs the compiler to set aside 4 byes of storage for the variable myInteger. Whenever you manipulate myInteger in a program, the compiler will manipulate the data at the memory location set aside for myInteger. Just as with variable names, there are some rules for the use of Dim as well.

Dim myVar As Integer
This will create a single integer-type variable.
Dim As Integer myVar, myVar2
This will create two integer-type variables.
Dim myVar As Double, myVar2 As Integer
This will create a double-type variable and an integer-type variable.
Dim myVar as Integer = 5
This will create an integer-type variable and set the value of the variable to 5.
Dim myVar As Double = 5.5, myVar2 As Integer = 5
This will create a double-type variable and set the value to 5.5, and an integer-type variable and set the value to 5.
Dim Shared as Integer myInt
This will create a shared (global) variable myInt accessible anywhere within your program.

In FreeBasic, variables must be declared before use when using -lang fb.

The Var Statement

The Var statement creates an implicit variable whose type is determined by the value of the initialization. For whole numbers, the default type in Integer. For numbers that contain a decimal point, the default type is Double. String initializers default to Zstring.

The initializer can be a literal value, or an expression. To set Var to specific data type you can use the Cast function to coerce the variable to the desired value. The following snippet shows some example initializer statements.

Var aInt = 1234 'default to integer
Var aDbl = 1234.56 'default to double
Var aUInt = Cast(UInteger, 1234)
Var aStr = "Hello World!"

Shared Variables

The Dim Shared version of Dim creates a shared variable. This means that any variable you create as Shared can be accessed anywhere within the program’s current module. To put it another way, the scope of a shared variable is module level scope. Scope refers to the visibility of a variable, where you can access a particular variable within a program.

The number of variables in your program (as well as the program itself) is limited only by your available memory, up to 2 gigabytes.

Static Variables

Static variables are used within subroutines and functions and retain their values between calls to the subroutine or functions. Static variables are useful for counters where you need to keep track of items while calling a subroutine or function multiple times.

Common Variables

Variables declared as Common can be shared between multiple code modules, that is between multiple bas files in the same program.

Extern and Import Variables

Extern and Import are used when creating DLL's and like Common, are designed to share variables in different modules.

Using Different Number Formats

Besides decimal numbers, FreeBasic is able to recognize numbers in hexadecimal, binary and octal formats.

Number Base Format
Decimal myVar = 254
Hexadecimal myVar = &HFE
Binary myVar = &B11111110
Octal myVar = &O376
Exponential Numbers myVar = 243E10

Hexadecimal Numbers

Hexadecimal is a base 16 numbering scheme and have digits in the range of 0 to F. Hexadecimal numbers are commonly used as constant values in the Windows API and many third party libraries as it is a compact way to represent a large value. To indicate a hexadecimal number, use the &H prefix.

Binary Numbers

Binary is a base 2 numbering scheme and have digits in the range of 0 and 1. Binary is the language of the computer. Although we can enter numbers and letters into the computer, it all must be translated into binary before the computer can understand it. To indicate a binary number, use the &B prefix.

Octal Numbers

Octal is a base eight numbering scheme and have digits in the range of 0 to 7. Octal numbers were very popular in early computer systems, but aren’t used much today except in some specialized applications. To indicate an octal number, use the &O prefix.

Exponential Numbers

You can use exponential numbers in your program by adding the E suffix followed by the power. To use the number 105, you would write the number as 10E05. You can directly set a double or single type variable using the exponent format. You can also use negative exponents such as 10E-5, which when printed to the screen would like 1.e-004.

Which Data Type To Use?

There are a number of different data types available, so how do you choose the right data type for any given application? The rule of thumb is to use the largest data type you need to hold the expected range of values. This may seem like stating the obvious, but many programs fail because the programmer did not fully understand the range of data in their program. When you crate a program, you should map out not only the logic of the program, but the data associated with each block of logic. When you map out the data ahead of time, you are less likely to run into data-type errors.

For example, if you were working with ASCII codes, which range from 0 to 255, an ubyte would be a good choice since the range of an ubyte is the same as the range of ASCII codes, and you are only using 1 byte of memory.

There is another consideration though, the “natural” data size of the computer. On a 32-bit system, the natural data size is 4 bytes, or an integer. This means that the computer is optimized to handle an integer, and does so more efficiently, even though you are “wasting” 3 bytes of memory by using an integer for an ASCII code.

In most cases an integer is a good general-purpose choice for integer data. The range is quite large, it handles both negative and positive numbers and you benefit from using the computer’s natural data type.

For floating point data, a double is a good choice since, like the integer, it has a good range of values and better precision than a single.

For large integer data you should use a uinteger for positive data or a longint for large negative and positive numbers. These are only suggestions however; what data type you end up using will be dictated by the needs of your program.

Variable Scope

Scope refers to the visibility of a variable, where you can access a variable within a program. Before you can understand the different levels of scope, you need to understand the structure of a program in FreeBasic.

Program Structure

A complete program is composed of one or more .bas files, called modules. Each module can contain both module level code, and code contained within subroutines and functions. Module level code is code that is not contained within a subroutine or function. The following snippet illustrates the various parts of a module.

Dim aInt as Integer 'Variable declared at module level

Sub DoSomething
    Dim aInt as Integer 'Variable declared at sub level

    ... 'This code is local to sub
End Sub

Function DoSomethingElse() as Integer
    Dim aInt as Integer 'Variable declared at func level

    ... 'This code is local to func
End Function

'Module level code
aInt = 5
aInt = DoSomethingElse()

Local Variables

If you define a variable at the module level (and not using Shared), the variable is said to have local module level scope. It is visible to the module level code, but not to any subroutine or function within the module. In the example above the module variable aInt is only visible to the module level code.

Variables defined within a subroutine or function are local to the subroutine or function and are not visible to module level code or any other subroutine or function.

Variables Defined Within Control Structures

Variables that are defined within If, For-Next, While-Wend and Do-Loop constructs are local to the control structure block code. That is, they are not visible outside the bounds of the begin and end of the control block, just like a variable declared within a subroutine or function.

Shared Variables

In the example, if you wanted aInt to be visible within the subroutine or function, you would need to declare the variable as Shared and then not declare a variable with the same name within any subroutine, function or control block. Shared variables are visible to module level code, subroutine or function level code and within control structure blocks.

Scope Conflicts

In the code snippet above, if aInt were declared as Shared, and each subroutine and function declared aInt, there would be a scope conflict, since there is one variable name used for different levels of scope.

The compiler resolves this by taking the current scope into account and mapping the variable within that scope. Since subroutines and functions have a lower level of scope than the module, aInt would refer to the variable declared within the sub or func, and not the one declared at the module level, even though it is declared as a shared variable.

Multiple Modules

Scope is limited to a single module, that is a single .bas file. However, it is sometimes necessary to extend the scope from one module to another. You would use the Common statement when you declare a variable that needs to be shared among modules.

Each module must have the same Common declaration in order for the compiler to match up the common variables. If you declare a variable in module1 as Common aInt as Integer then module2 must also have Common aInt as Integer. Without the common declaration aInt would not be visible within module2.

You can add the Shared attribute to Common, that is Common Shared to not only extend scope to multiple modules, but to extend scope within a module. Common Shared operates the same as Shared within a single module. As with Common, you need to have matching declarations in each module that needs access to the variable.

Scope…End Scope

You can create a temporary scope block by using the Scope, End Scope keywords. The scope block is very useful when creating multi-line macros where you may need to create some temporary working variables but do not want to introduce name conflicts in the program. The following snippet illustrates how to create a scope block.

    Dim tmp as Integer
    ... 'Some code
End Scope

The scope of any variable created within a scope block is limited to the block itself. However, the scope block inherits the visibility of the surrounding scope so that variables created at the same scope as the scope block are visible within the block.

For example, if you have aInt which is at module level scope, and the scope block is at module level scope, then aInt would be visible inside the scope block. Unless of course there is a scope conflict, in which case the variable inside the scope block would override the variable with the same name outside the scope block.

Variable Lifetime

Not only does scope set the visibility of a variable, it also determines the lifetime of a variable. A variable goes through several stages in its lifetime; creation, initialization, access and destruction. When this occurs depends on the scope of a variable, that is, where the variable has been defined within the program.

Module Level Variables

Module level variables exist for the life of a program, since they are declared within the main body of the program. Module level code is the main executing code of the program, and terminates when the program ends.

Subroutine and Function Level Variables

Variables declared within a subroutine and function exist as long as the program is within the body of the subroutine and function. On entering the sub/func, the variable is created, initialized and can be accessed within the sub/func. Once the subroutine or function exits, the variable is destroyed.

Static Variables

One exception to the declared sub/func variable is the Static variable. Static variables maintain their value between calls to the subroutine or function and so have a module level lifespan.

Control Block Variables

Variables declared within a control block, such as a For-Next, exist as long as the control block is executing. Upon leaving the control block, the variables are destroyed.

Scope…End Scope Variables

Variables declared within a scope block exist as long as the the scope block exists. Once the program leaves the scope block, any variables created within the scope block are destroyed.