3.1.1 Ordinal types

With the exception of floating point value types, all base types are ordinal types. Ordinal types have the following characteristics:

  1. Ordinal types are countable and ordered, i. e. it is, in principle, possible to start counting them one by one, in a specified order. This property allows the operation of functions as Inc, Ord, Dec on ordinal types to be defined.

  2. Ordinal values have a smallest possible value. Trying to apply the Pred function on the smallest possible value will generate a range check error if range checking is enabled.

  3. Ordinal values have a largest possible value. Trying to apply the Succ function on the largest possible value will generate a range check error if range checking is enabled.

  4. Integer ordinal types are mutually assignment compatible: there is no need to typecast when assigning an ordinal value of one (e.g. smallint) to another (e.g. byte). If you assign a value to a variable of an ordinal type, and that value is actually out of the range of valid values for that type, this will not result in a run-time error, although it may be a logical error. Only when range checking is enabled will such an assignment result in a range check error.

The fact that ordinals are mutually assignment compatible means that the following program will work without errors when compiled without range checking:

var
   b: byte;
   si: integer;
   w: word;
begin
   b := 33;
   si := 512;
   b := si; // out of range (too big)
   writeLn(b);  // shows zero!

   b := 33;
   si := -512;
   b := si; // out of range (negative)
   writeLn(b);  // shows zero!

   w := 33;
   si := -9;
   w := si; // Out of range (negative)
   writeLn(w);  // shows 65527!

   b := 33;
   si := 512;
   b := byte(si); // out of range (too big)
   writeLn(b);  // shows zero!
end.

Only when you compile with range checking by specifying -Cr on commandline, or adding {$R+} to the source code, will the compiler insert code to check whether the assigned value is valid for the target ordinal type.

In the last example, a typecast is used, forcing the right-hand side of the assignment to the same type as the left-hand side. In that case, no range checks will be inserted, even when range checking is enabled.

Remark Int64 and QWord are considered ordinal types on 64-bit CPUs. On 32-bit types they have some of the characteristics of ordinals, but they cannot be used e. g. in for loops.

Integers

A list of predefined integer types is presented in table (3.1).


Table 3.1: Predefined integer types

Name

Integer
Shortint
SmallInt
Longint
Longword
Int64
Byte
Word
Cardinal
QWord
ByteBool
WordBool
LongBool
QWordBool


The integer types, and their ranges and sizes, that are predefined in Free Pascal are listed in table (3.2). Please note that the qword and int64 types are not true ordinals, so some Pascal constructs will not work with these two integer types.


Table 3.2: Predefined integer types

Type Range Size in bytes



Byte 0 .. 255 1
Shortint -128 .. 127 1
Smallint -32768 .. 32767 2
Word 0 .. 65535 2
Integer either smallint or longint size 2 or 4
Cardinal longword 4
Longint -2147483648 .. 2147483647 4
Longword 0 .. 4294967295 4
Int64 -9223372036854775808 .. 9223372036854775807 8
QWord 0 .. 18446744073709551615 8




The integer type is an alias to the smallint type in the default Free Pascal mode. It is an alias for the longint type in either Delphi or ObjFPC mode. The cardinal type is currently always mapped to the longword type.

Remark The compiler decides on the type of an integer constant based on the value: An integer constant gets the smallest possible signed type. The first match in table (3.3) is used.


Table 3.3: Integer constant type mapping

Range Type


-128..127 Shortint
128..255 Byte
-32768..32767 Smallint
32768..65535 Word
-2147483648..2147483647 longint
2147483648..4294967295 Cardinal (longword)
-9223372036854775808 .. 9223372036854775807 Int64



That means constants in the range -128..127 are mapped to shortint, constants in range 128..255 are mapped to byte, etc. Constants in the range 2147483647..high(cardinal) are parsed as cardinals, and all decimal constants which do not fit either of the above ranges are parsed as 64-bit integer constants.

Remark In newer Delphi versions, the longint type is platform and CPU dependent. This is not so in FPC, where longint is 32-bit on all platforms.

As a pascal compiler, Free Pascal does automatic type conversion and upgrading in expressions where different kinds of integer types are used:

  1. Every platform has a “native” integer size, depending on whether the platform is 8-bit, 16-bit, 32-bit or 64-bit. E. g. on AVR this is 8-bit.

  2. Every integer smaller than the “native” size is promoted to a signed version of the “native” size. Integers equal to the “native” size keep their signedness.

  3. The result of binary arithmetic operators (+, -, *, etc.) is determined in the following way:

    1. If at least one of the operands is larger than the native integer size, the result is chosen to be the smallest type that encompasses the ranges of the types of both operands. This means that mixing an unsigned with a smaller or equal in size signed will produce a signed type that is larger than both of them.

    2. If both operands have the same signedness, the result is the same type as them. The only exception is subtracting (-): in the case of unsigned - unsigned subtracting produces a signed result in FPC (as in Delphi, but not in TP7).

    3. Mixing signed and unsigned operands of the “native” int size produces a larger signed result. This means that mixing longint and longword on 32-bit platforms will produce an int64. Similarly, mixing byte and shortint on 8-bit platforms (AVR) will produce a smallint.

Boolean types

Free Pascal supports the Boolean type, with its two predefined possible values True and False. These are the only two values that can be assigned to a Boolean type. Of course, any expression that resolves to a boolean value, can also be assigned to a boolean type.


Table 3.4: Boolean types

Name Size Ord(True)



Boolean 1 1
Boolean8 1 1
Boolean16 2 1
Boolean32 4 1
Boolean64 8 1
ByteBool 1 Any nonzero value
WordBool 2 Any nonzero value
LongBool 4 Any nonzero value
QWordBool 8 Any nonzero value




In addition to the simple Boolean type, the additional Boolean8, Boolean16, Boolean32 and Boolean64 types exist. These are in fact integer types, which are assignment-compatible with the simple boolean type. As an integer, the values for True and False are 1 and 0. This can be used to interface with C code that defines a boolean of this size with values 0 and 1.

To make interfacing with C even easier, Free Pascal also supports the ByteBool, WordBool, LongBool and QWordBool types. These are of type Byte, Word, Longint or Int64, but are again assignment compatible with a Boolean. The only difference with the Boolean8/16/32/64 types is in what values are considered true or false: The value False is equivalent to 0 (zero) and any nonzero value is considered True when converting to a boolean value. A boolean value of True is converted to Not(0) in case it is assigned to a variable of type ByteBool, WordBool, LongBool or QWordBool.

Assuming B to be of type Boolean, the following are valid assignments:

 B := True;
 B := False;
 B := 1<>2;  { Results in B := True }

Boolean expressions are also used in conditions.

Remark In Free Pascal, boolean expressions are by default always evaluated in such a way that when the result is known, the rest of the expression will no longer be evaluated: this is called short-cut boolean evaluation.

In the following example, the function Func will never be called, which may have strange side-effects.

 ...
 B := False;
 A := B and Func;

Here Func is a function which returns a Boolean type.

This behavior is controllable by the {$B } compiler directive.

Enumeration types

Enumeration types are supported in Free Pascal. On top of the Turbo Pascal implementation, Free Pascal allows also a C-style extension of the enumeration type, where a value is assigned to a particular element of the enumeration list.

_________________________________________________________________________________________________________
Enumerated types

--enumerated- type- (---|--identifier- list------) ----------------------
                    --assigned-enum--list---|
                            ,

--identifier- list--|identifier ------------------------------------------
              ----,----

--assigned-enum -list---|identifier- := - expression ------------------------
                  |- identifier -= - expression -- |
                  ------------,--------------
____________________________________________

(see chapter 12, page 599 for how to use expressions) When using assigned enumerated types, the assigned elements must be in ascending numerical order in the list, or the compiler will complain. The expressions used in assigned enumerated elements must be known at compile time. So the following is a correct enumerated type declaration:

Type
  Direction = ( North, East, South, West );

A C-style enumeration type looks as follows:

Type
  EnumType = (one, two, three, forty := 40,fortyone);

or you can use

Type
  EnumType = (one, two, three, forty = 40,fortyone);

The latter notation is mandatory in mode DELPHI.

As a result, the ordinal number of forty is 40, and not 3, as it would be when the ’:= 40’ wasn’t present. The ordinal value of fortyone is then 41, and not 4, as it would be when the assignment wasn’t present. After an assignment in an enumerated definition the compiler adds 1 to the assigned value to assign to the next enumerated value.

When specifying such an enumeration type, it is important to keep in mind that the enumerated elements should be kept in ascending order. The following will produce a compiler error:

Type
  EnumType = (one, two, three, forty := 40, thirty := 30);

It is necessary to keep forty and thirty in the correct order. When using enumeration types it is important to keep the following points in mind:

  1. The Pred and Succ functions cannot be used on this kind of enumeration types. Trying to do this anyhow will result in a compiler error.

  2. Enumeration types are stored using a default, independent of the actual number of values: the compiler does not try to optimize for space. This behavior can be changed with the {$PACKENUM n} compiler directive, which tells the compiler the minimal number of bytes to be used for enumeration types. For instance

         Type
         {$PACKENUM 4}
           LargeEnum = ( BigOne, BigTwo, BigThree );
         {$PACKENUM 1}
           SmallEnum = ( one, two, three );
         Var S : SmallEnum;
             L : LargeEnum;
         begin
           WriteLn ('Small enum : ',SizeOf(S));
           WriteLn ('Large enum : ',SizeOf(L));
         end.
    

    will, when run, print the following:

         Small enum : 1
         Large enum : 4
    

More information can be found in the Programmer’s Guide, in the compiler directives section.

Subrange types

A subrange type is a range of values from an ordinal type (the host type). To define a subrange type, one must specify its limiting values: the highest and lowest value of the type.

_________________________________________________________________________________________________________
Subrange types

--subrange- type-constant- ..- constant--------------------------------
____________________________________________

Some of the predefined integer types are defined as subrange types:

Type
  Longint  = $80000000..$7fffffff;
  Integer  = -32768..32767;
  shortint = -128..127;
  byte     = 0..255;
  Word     = 0..65535;

Subrange types of enumeration types can also be defined:

Type
  Days = (monday,tuesday,wednesday,thursday,friday,
          saturday,sunday);
  WorkDays = monday .. friday;
  WeekEnd = Saturday .. Sunday;

Character types

A character type is also an ordinal type: characters are ordered, can be counted. There are 2 character types:

AnsiChar

This is a 1-byte character. The interpretation of the character depends on the codepage.

WideChar

This is a 2-byte character. The interpretation of the character depends on the codepage.

Characters can be used in a loop, one can use prev and succ on it, as well as ord.

For more information on characters, see section 3.2, page 113.