Appendix B: Intrinsic Procedures Reference

A grouped, hand-verified reference to the Fortran intrinsic procedures you will reach for daily. An intrinsic is a function or subroutine built into the language — always available, with no use and no library to link (the one exception is the two intrinsic modules in §B.7, which do need a use). The compiler supplies them and often knows how to implement them with special hardware instructions, which is why you should prefer an intrinsic to a hand-written loop wherever one exists.

A few conventions make the tables below read correctly:

  • Examples assume the book's kind integer, parameter :: dp = selected_real_kind(15, 307), and real literals carry the _dp suffix (see Chapter 3).
  • Real results are shown as their mathematical value, to displayed precision. A transcendental result is the nearest representable double, so sqrt(2.0_dp) prints 1.414214 under an f12.6 descriptor even though the stored value differs far out in the last digit — exactly as in Chapter 3.
  • Most numeric functions are elemental: hand one an array and it is applied to every element, returning an array of the same shape (sqrt([1.0_dp, 4.0_dp, 9.0_dp]) is [1.0, 2.0, 3.0]).
  • String positions are 1-based; a returned position of 0 means "not found" (see Chapter 12).
  • Trigonometric functions use radians.
  • Kind numbers (returned by kind, selected_real_kind, selected_int_kind) are processor-dependent; the values shown are what gfortran reports. Logicals print as T/F but their values are .true./.false..

B.1 Numeric and mathematical functions

The numerical toolbox that made Fortran Fortran. All are elemental; the trig family takes radians; abs of a complex returns its modulus.

Name What it does Example → result
abs(x) absolute value; modulus for complex abs(-3.5_dp)3.5; abs((3.0_dp,4.0_dp))5.0
sqrt(x) square root (x ≥ 0 for real) sqrt(16.0_dp)4.0; sqrt(2.0_dp)1.41421356…
exp(x) $e^{x}$ exp(0.0_dp)1.0; exp(1.0_dp)2.71828182…
log(x) natural logarithm (x > 0) log(1.0_dp)0.0; log(2.0_dp)0.69314718…
log10(x) base-10 logarithm (x > 0) log10(1000.0_dp)3.0
sin(x), cos(x), tan(x) trig, argument in radians sin(0.0_dp)0.0; cos(0.0_dp)1.0
asin(x), acos(x), atan(x) inverse trig, result in radians acos(1.0_dp)0.0; atan(1.0_dp)0.78539816… ($\pi/4$)
atan2(y, x) angle of the point $(x, y)$, in $(-\pi, \pi]$ atan2(1.0_dp, 1.0_dp)0.78539816… ($\pi/4$)
sinh(x), cosh(x), tanh(x) hyperbolic functions cosh(0.0_dp)1.0; tanh(0.0_dp)0.0
max(a, b, …), min(a, b, …) largest / smallest of the arguments max(3, 7, 1)7; min(3, 7, 1)1
mod(a, p) remainder with the sign of the dividend a mod(7, 3)1; mod(-7, 3)-1
modulo(a, p) remainder with the sign of the divisor p modulo(7, 3)1; modulo(-7, 3)2
sign(a, b) magnitude of a with the sign of b sign(3.0_dp, -1.0_dp)-3.0
hypot(x, y) $\sqrt{x^2 + y^2}$, computed without overflow hypot(3.0_dp, 4.0_dp)5.0
x ** y exponentiation (an operator, not a function) 2.0_dp ** 101024.0; 2 ** 38

Note that ** associates right to left, so 2 ** 3 ** 2 is 2 ** (3 ** 2) = 2 ** 9 = 512, not 64 — parenthesize when it matters. The difference between mod and modulo shows up only for negative arguments and mirrors the truncate-vs-floor distinction of integer division; modulo is the one you want for periodic wraparound (Chapter 3).

The rounding-and-conversion family

Four intrinsics turn a real into a whole number, and the differences between them are a frequent source of off-by-one bugs. int truncates toward zero, nint rounds to nearest (ties away from zero), floor rounds toward $-\infty$, and ceiling rounds toward $+\infty$. Each returns a default integer (pass a kind= argument for a wider one). real(i) and dble(i) go the other way, integer → real:

x int(x) nint(x) floor(x) ceiling(x)
2.7_dp 2 3 2 3
2.5_dp 2 3 2 3
-2.7_dp -2 -3 -3 -2
Name What it does Example → result
int(x [, kind]) truncate toward zero → integer int(2.7_dp)2; int(-2.7_dp)-2
nint(x [, kind]) round to nearest integer (ties away from 0) nint(2.5_dp)3; nint(-2.5_dp)-3
floor(x [, kind]) round toward $-\infty$ → integer floor(2.7_dp)2; floor(-2.7_dp)-3
ceiling(x [, kind]) round toward $+\infty$ → integer ceiling(2.3_dp)3; ceiling(-2.3_dp)-2
real(a [, kind]) integer → real, or the real part of a complex real(5)5.0; real((3.0_dp,4.0_dp))3.0
dble(a) convert to double precision dble(5)5.0 (kind of 1.0d0)

B.2 Array functions

Fortran's array intrinsics let you replace a loop with a word (Chapter 5). Reductions (sum, count, any, …) collapse an array to a scalar; inquiries (size, shape, lbound) report its structure; the linear-algebra trio (matmul, dot_product, transpose) does the mathematics that elementwise * deliberately does not. Reductions take an optional mask= (sum only where a condition holds) and, for rank > 1, an optional dim= (reduce along one axis).

Name What it does Example → result
sum(a [, dim, mask]) sum of the elements sum([1,2,3])6
product(a [, dim, mask]) product of the elements product([1,2,3,4])24
maxval(a), minval(a) largest / smallest element maxval([5,9,2])9; minval([5,9,2])2
maxloc(a), minloc(a) location of the max / min (rank-1 result) maxloc([5,9,2])[2]; minloc([5,9,2])[3]
count(mask) how many mask elements are .true. count([1,2,3,4] > 2)2
any(mask), all(mask) is any / are all .true. any([1,2,3] > 2).true.; all([1,2,3] > 2).false.
size(a [, dim]) total elements, or the extent of one dimension size([1,2,3])3
shape(a) the shape, as a rank-1 integer array shape of a(3,4)[3, 4]
lbound(a [, dim]), ubound(a [, dim]) lower / upper index bounds for b(0:9): lbound(b,1)0, ubound(b,1)9
matmul(A, B) true matrix (or matrix–vector) product see the code below → [[19,22],[43,50]]
dot_product(x, y) $\sum_i x_i y_i$ of two rank-1 arrays dot_product([1,2,3],[4,5,6])32
transpose(A) swap rows and columns of a rank-2 array transpose of [[1,2],[3,4]][[1,3],[2,4]]
reshape(src, shp [, pad, order]) reshape a flat list into an array reshape([1,2,3,4],[2,2]) → columns [1,2],[3,4]
pack(a, mask [, vec]) gather masked elements into a rank-1 array pack([1,2,3,4], [1,2,3,4] > 2)[3, 4]
unpack(vec, mask, field) scatter a vector back under a mask unpack([9,9], [.true.,.false.,.true.], [0,0,0])[9,0,9]
spread(src, dim, n) replicate, adding one dimension spread(7, 1, 3)[7, 7, 7]
cshift(a, sh [, dim]) circular shift cshift([1,2,3,4], 1)[2,3,4,1]
eoshift(a, sh [, bnd, dim]) end-off shift, filling with bnd (default 0) eoshift([1,2,3,4], 1)[2,3,4,0]
merge(t, f, mask) elementwise pick: t where mask, else f merge([1,2,3],[10,20,30],[.true.,.false.,.true.])[1,20,3]
norm2(a) Euclidean (L2) norm $\sqrt{\sum_i a_i^2}$ norm2([3.0_dp, 4.0_dp])5.0

Two layout traps worth stating explicitly. reshape fills its result in array-element (column-major) order by default — reshape([1,2,3,4],[2,2]) puts 1,2 down the first column and 3,4 down the second, so as a matrix its rows are [1,3] and [2,4]. Pass order=[2,1] to fill row-by-row instead. And maxloc/minloc return the position, not the value, as a rank-1 array (add dim=1 for a plain scalar). The matmul/transpose example, with the same row-wise order=[2,1] trick Chapter 5 uses so the literals read like the matrices they build:

integer :: A(2,2), B(2,2)
A = reshape([1,2, 3,4], [2,2], order=[2,1])   ! rows [1,2] then [3,4]
B = reshape([5,6, 7,8], [2,2], order=[2,1])   ! rows [5,6] then [7,8]
! matmul(A, B)   -> rows [19,22] then [43,50]   (19 = 1*5 + 2*7, etc.)
! transpose(A)   -> rows [1,3]  then [2,4]
! matmul(A, [1,1]) -> [3, 7]     (each row summed)

For large or performance-critical linear algebra, matmul is the on-ramp and LAPACK/BLAS is the highway — see Chapter 21.


B.3 Character and string functions

The everyday plumbing of text: measure a string (len, len_trim), clean it (trim, adjustl, adjustr), search it (index, scan, verify), and build it (repeat, //). Search functions return a 1-based position, or 0 for "not found"; each accepts an optional back=.true. to search from the right. Full treatment in Chapter 12.

Name What it does Example → result
len(s) declared/allocated length, including trailing blanks len('hello')5
len_trim(s) length excluding trailing blanks (0 if all blank) len_trim('hi ')2
trim(s) copy with trailing blanks removed trim('hi ')'hi' (length 2)
adjustl(s) left-justify: leading blanks moved to the tail (length kept) adjustl(' ab')'ab '
adjustr(s) right-justify: trailing blanks moved to the front (length kept) adjustr('ab ')' ab'
index(s, sub [, back]) position where sub first occurs, else 0 index('abcabc','b')2; index('abcabc','b',.true.)5
scan(s, set [, back]) position of the first char that is in set, else 0 scan('key=val','=')4
verify(s, set [, back]) position of the first char not in set, else 0 verify('2018','0123456789')0; verify('20x8','0123456789')3
repeat(s, n) n concatenated copies of s repeat('ab', 3)'ababab'
achar(i) the character at ASCII code i achar(65)'A'; achar(97)'a'
iachar(c) the ASCII code of character c iachar('A')65; iachar('0')48
s1 // s2 concatenation operator (length = sum of lengths) 'heat' // '.vtk''heat.vtk'

A rule from Chapter 12: concatenation splices operands exactly as given, blanks and all, so trim a fixed-length variable before you join it — trim(dir) // '/data.txt', never dir // '/data.txt' — or its padding rides along. index, scan, and verify return an integer position, not a logical; test index(s, sub) > 0 for "present," never if (index(...)).

achar/iachar are pinned to the ASCII collating sequence (portable and what you almost always want); their cousins char/ichar use the processor's default character set instead.


B.4 Kind and numeric-inquiry functions

These answer "what can this type represent?" — indispensable for portable, precision-aware numerical code (Chapter 3; Chapter 20 owns the floating-point theory behind epsilon, huge, and tiny). The argument is used only for its type and kind — its value is irrelevant, so huge(1.0_dp) and huge(x) for any real(dp) :: x are identical.

Name What it does Example → result
kind(x) the kind number of x (processor-dependent) gfortran: kind(1.0)4; kind(1.0d0)8
selected_real_kind(p, r) a real kind with ≥ p digits and range to $10^{r}$ (or < 0 if none) selected_real_kind(15, 307)8 (gfortran)
selected_int_kind(r) an integer kind holding every value up to r digits selected_int_kind(18)8 (gfortran)
epsilon(x) machine epsilon: spacing of the model just above 1 epsilon(1.0_dp)2.22e-16 ($2^{-52}$)
huge(x) the largest representable value of x's kind huge(1)2147483647; huge(1.0_dp)1.80e308
tiny(x) the smallest positive normalized real tiny(1.0_dp)2.23e-308 ($2^{-1022}$)
precision(x) decimal digits of precision precision(1.0_dp)15; precision(1.0)6
range(x) decimal exponent range range(1.0_dp)307; range(1)9
digits(x) number of significant digits in the model's radix digits(1.0_dp)53; digits(1)31
radix(x) the base of the model (2 on all IEEE hardware) radix(1.0_dp)2
spacing(x) absolute spacing of model numbers near x (one ULP) spacing(1.0_dp)2.22e-16 (= epsilon at 1.0)
nearest(x, s) the nearest different machine number toward sign(s) nearest(1.0_dp, 1.0_dp) → next double above 1.0 (≈ 1.0 + 2.22e-16)

The reason to write selected_real_kind(15, 307) rather than a bare real(8) is precisely that the kind number 8 is a gfortran-specific detail, whereas "15 digits, range to $10^{307}$" is the portable scientific requirement. The named kinds in iso_fortran_env (§B.7) are the other portable route.


B.5 Bit manipulation (brief)

Fortran treats a default integer as a vector of bits for these operations; bit positions are 0-based, counting from the least-significant bit. Handy for flags, masks, hashing, and low-level interop. The examples use small values so you can check them in binary (12 = 1100, 10 = 1010).

Name What it does Example → result
iand(i, j) bitwise AND iand(12, 10)8 (1100 ∧ 1010 = 1000)
ior(i, j) bitwise (inclusive) OR ior(12, 10)14 (1100 ∨ 1010 = 1110)
ieor(i, j) bitwise exclusive OR (XOR) ieor(12, 10)6 (1100 ⊕ 1010 = 0110)
ishft(i, sh) logical shift: left if sh > 0, right if sh < 0, zero-filled ishft(3, 2)12; ishft(16, -2)4
btest(i, pos) is bit pos set? (logical) btest(12, 2).true.; btest(12, 0).false.
ibset(i, pos) return i with bit pos set to 1 ibset(0, 3)8
ibclr(i, pos) return i with bit pos cleared to 0 ibclr(15, 0)14
popcnt(i) population count: number of 1-bits popcnt(7)3; popcnt(255)8

Related members of the family you may meet: ibits(i, pos, len) (extract a bit field), mvbits (move a bit field, a subroutine), ishftc (circular shift), trailz/leadz (count trailing/leading zero bits), and bit_size(i) (the number of bits in the type — 32 for a default integer).


B.6 Type conversion, allocation, and program environment

A grab-bag of intrinsics for reinterpreting data, querying the status of dynamic objects, and reading the command line.

Name What it does Example → result
transfer(src, mold) reinterpret the bits of src as the type of mold (no conversion) transfer(1.0, 0)1065353216 (raw IEEE-754 32-bit pattern of 1.0)
allocated(a) is the allocatable a currently allocated? (logical) .true. after allocate, .false. before / after deallocate
associated(p [, tgt]) is pointer p associated? (optionally, with tgt) .true. after p => target — see Ch. 11
present(arg) was this optional dummy argument supplied? (logical) .true. / .false. — see Ch. 6
is_contiguous(a) does a occupy contiguous memory? (logical) .true. for a whole array; .false. for a strided section a(1:10:2)
command_argument_count() number of command-line arguments (excludes the program name) for ./prog a b c3
get_command_argument(n, value [, length, status]) retrieve argument n into value (a subroutine) call get_command_argument(1, arg) puts the first argument in arg

transfer is the standard, portable way to read the raw bit pattern of one type as another (its result above is the exact IEEE-754 pattern 0x3F800000 of a 32-bit real 1.0, so the specific integer assumes a 32-bit default real). Contrast it with int/real, which convert the value; transfer copies the bits unchanged. allocated, associated, and present are the three status tests that make defensive code safe: check before you touch. command_argument_count and get_command_argument are the modern (Fortran 2003) replacements for the old nonstandard iargc/getarg extensions.


B.7 Two essential intrinsic modules

Unlike everything above, these two require a use — but they are part of the standard, shipped with every conforming compiler.

iso_fortran_env provides portable, self-documenting names for kinds and I/O units, so you never hardcode a compiler-specific number. Import only what you need:

use, intrinsic :: iso_fortran_env, only: real64, int64, error_unit, output_unit, compiler_version
Name What it is
real32, real64, real128 kind parameters for 32-, 64-, 128-bit reals (real64 is the usual dp)
int8, int16, int32, int64 kind parameters for 8- to 64-bit integers
input_unit, output_unit, error_unit the pre-connected unit numbers for stdin, stdout, and stderr
compiler_version(), compiler_options() strings describing the compiler and the flags used to build
iostat_end, iostat_eor the iostat values signalling end-of-file and end-of-record

Writing a diagnostic to standard error, for instance, is write(error_unit, '(a)') 'bad config' — portable because you named the unit rather than guessing its number. Many codes simply write use iso_fortran_env, only: dp => real64 as an alternative to selected_real_kind.

ieee_arithmetic exposes the IEEE-754 machinery for detecting and handling exceptional floating-point values — essential when a computation can produce a NaN or Inf (Chapter 20):

Name What it does Example → result
ieee_is_nan(x) is x a NaN (not-a-number)? ieee_is_nan(0.0_dp/0.0_dp).true.
ieee_is_finite(x) is x finite (neither Inf nor NaN)? ieee_is_finite(1.0_dp).true.

The module offers much more (ieee_value to construct a NaN or infinity, ieee_set_halting_mode and the exception flags to trap or poll invalid/overflow/divide_by_zero), but ieee_is_nan and ieee_is_finite are the two you will use most: a single if (ieee_is_nan(residual)) guard turns a silent NaN that quietly poisons a whole simulation into an early, locatable stop. Pair it with the -ffpe-trap=invalid,zero,overflow flag from Appendix C to catch the exception at its source.


Every result in this appendix was computed by hand, not run. If you find a discrepancy with your compiler — most likely a processor-dependent kind number in §B.4, or the exact printed digits of a transcendental in §B.1 — the compiler is right about its own kinds, and the mathematical value shown here is the intent.