Fortran Type System

Fortran is statically and strongly typed: every name has one type decided at compile time, and the compiler rejects nonsense mixes. The twist that confuses newcomers is the KIND parameter — types come in hardware variants, and the language gives you portable tools to select them.

The five intrinsic types

Fortran ships exactly five built-in types; arrays, strings and user records are built on top of them.

TypeValues it holdsDefault sizeExample literal
integerwhole numbers32 bit (often kind 4)-42
realfloating-point numbers32 bit (kind 4)3.14159
complextwo reals, real + imaginary2 × 32 bit(1.0, -2.5)
logical.true. or .false.compiler-dependent.true.
charactera fixed-length string of characters1 byte per char"winter"

Numeric types

Notable Fortran habits: division of two integers truncates (7/2 is 3), and exponentiation is a right-associative ** (2**10 is 1024). Integer/real mixes promote to real — so write 1.0_dp / 3, not 1 / 3, when you want a third.

program numeric_types
  implicit none
  integer :: count = 3          ! a whole number
  real    :: pi = 3.14159       ! single precision by default
  complex :: z                  ! a point in the complex plane
  z = (1.0, -2.5)
  print *, count, pi, z
  print *, 7 / 2                ! integer division: prints 3
  print *, 7.0 / 2              ! real division: prints 3.5
  print *, 2**10                ! exponentiation: prints 1024
end program numeric_types

KIND and precision

A real is not "a real" — it is a hardware representation chosen by the compiler from an implementation-defined set. The kind parameter is the integer tag that names one representation. Default kinds are the compiler's free choice, which is why a program that prints 32-bit floats on one compiler may print 64-bit on another. Scientific code must not rely on defaults; it must ask for a capability.

Real and integer KIND widths and ranges

Fig. 2 — KIND selects a representation; the language never specifies bytes, only capability.

Portable kind selection

Two mechanisms do the asking. selected_real_kind(p, r) returns the kind that provides at least p decimal digits and exponent range r — usually 8 (double precision), sometimes 16. The module iso_fortran_env defines symbolic names for the compiler's concrete kinds: real32, real64, real128 and int8, int16, int32, int64. The idiomatic hook is a module parameter the whole project shares:

module kinds
  ! One place decides precision for the entire codebase.
  implicit none
  integer, parameter :: dp = selected_real_kind(15, 307)  ! ~double precision
  integer, parameter :: ip = selected_int_kind(9)         ! ~32-bit integers
end module kinds

program precision_demo
  use kinds, only: dp        ! import exactly the constants we need
  implicit none
  real(dp) :: x              ! x is double precision on every platform
  real(real64) :: y          ! alternative: iso_fortran_env names
  x = 1.0_dp                ! literal suffix: same kind as dp
  y = 0.1_real64
  print *, x, y
end program precision_demo

Three habits prevent the classic precision bugs: (1) define dp/ip once in a module; (2) suffix every real literal with its kind (1.0_dp); (3) never write double precision in new code — it is a legacy alias for the default real kind and silently changes meaning with compiler flags.

Logical and character types

A logical holds .true. or .false. and combines with the dotted operators .and. .or. .not. .eqv. .neqv. — the expressions lesson covers truth tables. A character variable has a compile-time length: character(len=20) :: name or the shorthand character(20). Assignment pads or truncates to fit silently, which surprises people used to resizable strings. For data of truly unknown length, Fortran 2003 added the deferred-length form character(len=:) :: s combined with allocatable, used in the strings lesson:

program characters
  implicit none
  character(len=12) :: short      ! exactly twelve characters
  character(len=:), allocatable :: long   ! grows to fit
  short = "hello"
  long  = short // " world"       ! // concatenates strings
  print *, len(short), trim(short), len(long)
end program characters

Trailing spaces are the price of fixed length: short holds "hello......" with seven trailing blanks, which is why output almost always passes through trim. The strings lesson systematizes these operations.

Declaration syntax in full

The complete recipe is type[, attributes] :: name[(shape)][ = init][, name2...]. Attributes — parameter, allocatable, pointer, dimension, intent, save — modify how the name behaves and are covered where they matter. Two attributes belong in every program from day one: implicit none at the unit top, and parameter for every magic number, so pi, g and steps are named once and optimized to literals.