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_dpsuffix (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)prints1.414214under anf12.6descriptor 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
0means "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 asT/Fbut 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 ** 10 → 1024.0; 2 ** 3 → 8 |
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
trima fixed-length variable before you join it —trim(dir) // '/data.txt', neverdir // '/data.txt'— or its padding rides along.index,scan, andverifyreturn an integer position, not a logical; testindex(s, sub) > 0for "present," neverif (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 c → 3 |
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.