In chapter 2, I mentioned that variables of built-in types can be explicitly and portably declared using specific type kinds. Type kind parameters determine the space that numeric variables occupy in memory, which in turn limits the range for integers, and the range and precision for real and complex numbers. Most Fortran compilers by default declare 4-byte-long integers and reals, equivalent to int and float in C, respectively. However, the standard doesn’t guarantee that the size will be the same between different systems and compilers. This is where the iso_fortran_env module comes in. It provides, among other things, a set of parameters you can use to specify the size of numeric data types (table 4.1).
Table 4.1 A summary of Fortran’s built-in numeric type kinds in iso_fortran_env
| Type kind | Type | Size (bytes) | C-equivalent |
|---|---|---|---|
int8 | integer | 1 | None |
int16 | integer | 2 | short |
int32 | integer | 4 | int |
int64 | integer | 8 | long |
real32 | real, complex | 4 | float |
real64 | real, complex | 8 | double |
real128 | real, complex | 16 | long double |
These type kinds are defined in the standard and are guaranteed to have a specified size in memory. Most commonly used Fortran compilers, such as GNU and Intel, fully support these type kinds. If your compiler doesn’t implement a standard-defined type kind, it will raise a compile-time error. The standard guarantees portability of data types in terms of the memory that they occupy (32 bits, 64 bits, and so on) but not in terms of their range (minimum and maximum values) and precision (how many significant digits can be represented). Unlike C, Fortran doesn’t have unsigned integer types. More on Fortran interoperability with C in chapter 11.
To declare a variable with a specific type kind parameter, provide it as a kind argument:
use iso_fortran_env ❶
integer(kind=int32) :: n ❷
real(kind=real32) :: dt ❸
❶ Accesses only the specified constants from a built-in module
❷ Declares n as a 4-byte (32-bit) integer
❸ Declares dt as a 4-byte (32-bit) real
In the first line, we access the iso_fortran_env module, like we did in the previous example of printing the compiler version and options. Then, we use the built-in parameters int32 and real32 to declare portable integer and real variables. In doing so, we ensure that both n and dt have a size of 4 bytes, across platforms and compiler vendors. The kind keyword is optional, so you can declare these more concisely as
integer(int32) :: n
real(real32) :: dt
In this shorter syntax, you specify the type kind in parentheses. From here on, I’ll omit the kind keyword in declarations for brevity. I’ll also use integer and integer(int32) interchangeably, because on all compilers and platforms I’m aware of, they’re one and the same in practice. Thus, if you prefer less verbose code and don’t need to use long integers like int64, it’s okay to declare them with just integer.
Tip Always use the portable type kind parameters provided by iso_fortran _env to declare your variables, at least for your real and complex variables.
I mentioned in the previous section that when you use iso_fortran_env, you import all the variables and procedures from that module, not just those that you intend to use in your program. There are two issues with this. First, if your program (or procedure) declares a variable with the same name as some entity that’s declared in the module, you may end up with a name conflict. I’ll describe in a bit how this occurs and how to prevent it, but for now, let’s just assume that you don’t want it to happen. Second, if your program (or procedure) references many different procedures and variables that were imported from a module, it’s difficult to see where the procedures and variables are defined just by looking at the code. This makes a program more difficult to understand and debug. However, if you import only specific variables and/or procedures from a module, it’ll be much easier to avoid name conflicts with your own variables, and the code will be more readable.
Back to our example of portable type kind parameters; let’s import only the ones that we want to use: int32 and real32. We’ll do so with the special variant of the use statement:
use iso_fortran_env, only: int32, real32 ❶
❶ Imports only these entities from the module
In this example, we imported two integer constants, int32 and real32, from the built-in module iso_fortran_env. We used the keyword use to access the module, and the keyword only to explicitly list only the items that we needed. This is analogous to Python’s from numpy import ndarray.
Tip Always use the use ..., only: ... syntax to import specific entities from a module. This will help you avoid name conflicts and will make your code easier to read and understand.
Now you know how to import variables from modules, and also how type kind parameters work. Can you help improve the variable declarations in the tsunami simulator by using the portable type kind parameters?
Exercise 1: Using portable type kinds in the tsunami simulator
You now know how to access iso_fortran_env for portable type kinds, as well as how to use them to declare your variables with portable types. Can you take our latest version of the tsunami simulator (listing 3.19), and make all declarations use portable type kinds?
You can find the solution to this exercise in the “Answer key” section near the end of this chapter.