Day 3

This is the day 3 page for computer programming course 101, an introductions to computer programming. Day 3 introduces the concept of types.

Below are the topics and course materials we are learning today.

A closer look at the Lazarus IDE

In this section we take a closer look at the Lazarus IDE. We go through some of the menus and options to familiarize the student with some of the basic functionality in the IDE. We go through some options to demonstrating how they work. We also show how the IDE includes its source code and can be rebuilt.

We install the terminal and todo list enhancements, rebuilding the IDE. We demonstrate how they work.

What is a type

Free Pascal is a strictly typed language. In terms of programming languages, types are strict definitions associated with variables and constants which both the IDE and compiler can see. These definitions enforce the rules used when working with variables which determine how your variables can be used.

So far we've used the Integer and string types. The strict typing rules in the compiler determine how these types can be used. For example the type rules allow us to add two integers together like so:
var
  A, B, C: Integer;
begin
  WriteLn('Enter two numbers and I will add them together');
  ReadLn(A);
  ReadLn(B);
  C := A + B; // OK, the type system says we can add two Integers
  WriteLn('The sum of ', A, ' + ', B, ' = ', C);
end.
Notice in line 7 we add integers A and B. This is valid syntax because the Integer type has defined rules allowing the add operator + to work upon two integer types.

Now suppose we were to change the type of C to a string. What do you suppose would happen?
var
  A, B: Integer;
  C: string;
begin
  WriteLn('Enter two numbers and I will add them together');
  ReadLn(A);
  ReadLn(B);
  C := A + B; // What happens now?
  WriteLn('The sum of ', A, ' + ', B, ' = ', C);
end.
Both the IDE and the compiler will generate an error. The error is telling us that two integers add up to another integer, and not a string. This is what the strict type system does. It makes sure that we are using our variables in the correct manner before we can successfully compile our programs. The strict type checking helps us by finding problems in our source code ahead of time.

If we want to work on types correctly we need to explicitly convert them if the type rules say we are violating the rules. We could fix the above example by converting the integer result of A + B like so:
var
  A, B: Integer;
  C: string;
begin
  WriteLn('Enter two numbers and I will add them together');
  ReadLn(A);
  ReadLn(B);
  C := IntToStr(A + B); // OK, we can explicitly convert A + B to a string
  WriteLn('The sum of ', A, ' + ', B, ' = ', C);
end.
Try the above examples in the Lazarus IDE and see how it reacts to incorrect types.

The built in types

The Free Pascal language contains several readily available types. Later we'll show you how you can build your own types, but for now we'll stick to these predefined types.

String and character types which can be concatenated using the add + operator. Multiple characters concatenated together build a string. Characters can be extracted from a string using square bracket [CharIndex] symbols and an indexer. A number can be converted to a character using the Char function. All string and character types can be compared using these comparison < = > <> operators resulting in a boolean type.
Char                 WideChar              string               WideString
Ordinal types which include booleans, unsigned, and signed integers.

Boolean types can be used with the logical operators and not or xor. When other types use comparison < = > <> operators, the result is a boolean value.

Integer types are whole numbers which can be signed or unsigned. An unsigned whole number can only have a positive value. The various integer types can be used interchangeably. They can use the following math + - * div operators. Notice the divide operator / operator is absent from this list. This is because true division of an integer may result in a fractional value, and thus converts the type to a floating point value.
Boolean              ByteBool              WordBool             LongBool
Byte                 Word                  LongWord             QWord                Cardinal
ShortInt             SmallInt              LongInt              Int64                Integer
Comparison < = > <> operators between both integer and float types can be freely combined. And finally, the following are bitwise and not or xor shl shr operators can be used on integer types.

Floating point types are real numbers which may have a fractional value. The word float describes how the decimal point . can move or float left or right. These types differ in the precision they allow, and also the size they consume in memory. The more precise floating point numbers consume more bytes. The following operators are allowed on floating point types + - * /. Notice we've included the divide / operator.
Single               Double                Extended             Currency

Pop quiz: the logical and comparison operators

Although we haven't delved much into the logical and comparison operators, let's test your current knowledge on the subject. Each of the following questions has either a True or False answer. Can you guess which one it is?

1. What is 1 = 1?

2. What is 1 + 2 = 4?

3. What is 6 - 4 > 2?

4. If X = 6 and Y = 10 what is (X > 6) and (Y < 10)?

5. Given the same X and Y values what is (X + 4 < Y) or (Y + 4 < X)?

6. Using those same values again values what is not (X > Y)?

7. What is 'Hello' > 'World'?

8. What is 'Hello' <> 'World'?

9. What is ('Cat' > 'Dog') and ('Ball' > 'Bat')?

10. What is 'a' > 'B'?

Every type has its size

Every type has a size in bytes which can be determined using the built in SizeOf function. For example you can write out the size of a few data types using a piece of code like this:
var
  B: Byte;
begin
  WriteLn('The size of a Byte is ', SizeOf(B));
  WriteLn('The size of a Word is ', SizeOf(Word));
  WriteLn('The size of an Integer is ', SizeOf(Integer));
end.
When you run the above in a program you will see the following output:
The size of a Byte is 1
The size of a Word is 2
The size of an Integer is 4
The number values in the listing above are the size in bytes of exactly how much memory is consumed by a value of each type. Note in our example that the SizeOf function operates on both variable and type names.

In addition to size, ordinal types have lower and upper ranges. The range of an ordinal is often related to it size. The range of an ordinal can be obtain through the Low and High functions. Here is an example that prints the range of some ordinal types.
begin
  WriteLn('Byte has a lower range of ', Low(Byte), ' and an upper range of ', High(Byte));
  WriteLn('SmallInt has a lower range of ', Low(SmallInt), ' and an upper range of ', High(SmallInt));
  WriteLn('LongWord has a lower range of ', Low(LongWord), ' and an upper range of ', High(LongWord));
  WriteLn('LongInt has a lower range of ', Low(LongInt), ' and an upper range of ', High(LongInt));
end.
When you run the above in a program you will see the following output:
Byte has a lower range of 0 and an upper range of 255
ShortInt has a lower range of -128 and an upper range of 127
LongWord has a lower range of 0 and an upper range of 4294967295
LongInt has a lower range of -2147483648 and an upper range of 2147483647
There are several other special functions for ordinals including Ord, Pred, and Succ. We will talk more about those functions a little later.

Programming break: printing binary numbers

Let's take a few minutes to try out some of the information from the previous section. Here is a program that converts integer types to a string representation of the numbers in binary form.

{ The BinaryNumbers program displays the binary representation of integers  }

program BinaryNumbers;

{$macro on}
{$define Ordinal := Byte}
{$define Name := 'Byte'}

{ The BinaryWrite procedure prints a value in binary }

function IntToBinary(Value: Ordinal): string;
var
  I: Integer;
begin
  { Initialize our result to an empty string }
  Result := '';
  { Calculate the number of bits for the size of our data }
  I := SizeOf(Value) * 8;
  {  While we still have bits to convert }
  while I > 0 do
  begin
    { Test if the least significant bit is a 1, see https://goo.gl/Laa3RH }
    if Value and 1 = 1 then
      Result := '1' + Result
    else
      Result := '0' + Result;
    { Shift all the bits in our value one to thr right }
    Value := Value shr 1;
    { And decrement the bit counter }
    I := I - 1;
  end;
end;

var
  A, B, C: Ordinal;
begin
  { Display our welcome message }
  WriteLn('Welcome to the binary number program');
  WriteLn('This size of a ' + Name + ' is ', SizeOf(Ordinal), ' byte(s) or ',
  	SizeOf(Ordinal) * 8,' bits');
  WriteLn('');
  { Ask for two numbers }
  WriteLn('Please enter your first ' + Name + ' value:');
  ReadLn(A);
  WriteLn('Please enter your second ' + Name + ' value:');
  ReadLn(B);
  WriteLn('');
  { Display the two numbers in binary form }
  WriteLn('These are your two numbers in binary form [press enter]');
  WriteLn(IntToBinary(A), ' = ', A);
  WriteLn(IntToBinary(B), ' = ', B);
  ReadLn();
  { Perform and print the following math operations + - * div }
  WriteLn('This is how your numbers look in binary after "+ - * div" math operations [press enter]');
  C := A + B;
  WriteLn(IntToBinary(C), ' = ', A, ' + ', B, ' = ', C);
  C := A - B;
  WriteLn(IntToBinary(C), ' = ', A, ' - ', B, ' = ', C);
  C := A * B;
  WriteLn(IntToBinary(C), ' = ', A, ' * ', B, ' = ', C);
  C := A div B;
  WriteLn(IntToBinary(C), ' = ', A, ' div ', B, ' = ', C);
  ReadLn();
  { Perform and print the following bitwise operations and or xor not }
  WriteLn('This is how your numbers look in binary after "and or xor not" bitwise operations [done]');
  C := A and B;
  WriteLn(IntToBinary(A), ' and');
  WriteLn(IntToBinary(B), ' =');
  WriteLn(IntToBinary(C));
  WriteLn('');
  C := A or B;
  WriteLn(IntToBinary(A), ' or');
  WriteLn(IntToBinary(B), ' = ');
  WriteLn(IntToBinary(C));
  WriteLn('');
  C := A xor B;
  WriteLn(IntToBinary(A), ' xor');
  WriteLn(IntToBinary(B), ' =');
  WriteLn(IntToBinary(C));
  WriteLn('');
  C := not A;
  WriteLn(IntToBinary(A), ' not =');
  WriteLn(IntToBinary(C));
  WriteLn('');
  C := not B;
  WriteLn(IntToBinary(B), ' not =');
  WriteLn(IntToBinary(C));
end.

What is an appropriate type

As explained earlier, built in types of the same category can be used together. You can add a Byte to and Integer, and you can add a Char to a string. But you may have noticed from the output of of an earlier example that many types are smaller in size than others. For example a Word is of size 2, and a Byte is of size 1.

So then you might ask yourself what happens to the extra information in the Word when it's assigned to a Byte type? Consider the following listing. What do you think its output will be and why?
var
  W: Word;
  B: Byte;
begin
  W := 300;
  B := W;
  WriteLn('The Word has a value of ', W, ' and the Byte has a value of ', B);
end.
Run the above as a program to find out the answer. To understand what's going on let's examine W and B in binary form.
W = 00000001 00101100
B =          00101100
As you can see, when a larger data type is assigned to a data type, the extra bits are dropped. This is called data loss. To avoid data loss, you should choose a type that is appropriate for the minimum and maximum values you expect to need.

An example where bytes might be appropriate is color values. Consider the HTML color code #A0FFC8. These are actually byte values for red, green, and blue byte components with the values 160, 255, and 200 respectively.

But why, you might ask, do people use byte types to store color components? Consider you requirements. The human eye can distinguish between approximately 7 million various colors. When we combine three bytes worth of color components we end up with over 16 million combinations. Now consider the 1080p standard computer resolution. How much memory would be required to hold an image of that size when using Byte sized RGB components? How much memory would that be for Word sized RGB components?

Homework

This day your homework is to write a command line program using the following requirements:
  • The source code file name must be couse101-day003.pas
  • Program name must be Ordinals
  • The program will print the name of every ordinal type
  • Underneath each type name print the size of and lower and upper range of the associated type

See also