Chapter 3 — Key Takeaways (Variables, Types, and Arithmetic)

A one-page reference. Types, kinds, the integer-division trap, and the arithmetic you will use in every program from here on.

The six intrinsic types

Type Holds Example declaration Notes
integer whole numbers, exact integer :: n = 42 default ≈ 32-bit, range ±2.1×10⁹
real floating-point, approximate real(dp) :: t plain real is single (~7 digits) — avoid in numerics
double precision ~15-digit real (legacy keyword) double precision :: x prefer real(dp); you'll read this in old code
complex a pair $a + b\,i$ complex :: z = (3.0, 4.0) real(z), aimag(z), abs(z)=modulus
logical .true. / .false. logical :: ok = .true. prints as T/F under l1
character fixed-length text character(len=5) :: s length is fixed (deferred-length → Ch. 12)

Kinds and portable precision (the one habit that matters most)

integer, parameter :: dp = selected_real_kind(15, 307)   ! ≥15 digits, range 10^307
real(dp) :: t                                            ! declare reals like this
t = 0.5_dp                                               ! literals get the _dp suffix
  • Never hardcode a kind number (real(8)): the number is compiler-specific. Request the requirement.
  • selected_real_kind(p, r) → real kind with ≥p digits and range ≥10^r (negative if impossible).
  • selected_int_kind(r) → integer kind holding all values up to r decimal digits (e.g. (18) → 64-bit).
  • Shortcut: use, intrinsic :: iso_fortran_env, only: dp => real64 (the 64-bit kind, by bit width).
  • precision(x) → decimal digits; range(x) → decimal exponent range; kind(x) → the raw kind number.

Arithmetic, precedence, and mixed-mode

  • Operators: ** (exponentiation) → * /+ -; parentheses override. ** is right-associative: 2**3**2 = 2**(3**2) = 512.
  • Mixed-mode: in each operation, the lower type is promoted before computing. Ranking: integerrealdoublecomplex. 2 * 3.06.0 (real).
  • The type of a subexpression is set by its operands, not by where the result is assigned.

The integer-division trap ⚠️ (the #1 beginner bug)

Expression Result Why
1 / 2 0 integer ÷ integer, truncates toward zero
7 / 2 3 3.5 truncated
-7 / 2 -3 truncates toward zero (not floor → not -4)
real(dp) :: x; x = 1/2 0.0 1/2 computed as integer first, then widened
real(1, dp) / real(2, dp) 0.5 convert an operand before dividing
1.0_dp / 2.0_dp 0.5 real literals → real division

Rule: for a real result, make sure at least one operand of every / is real (real(k, dp) or a _dp literal). A real variable on the left never rescues integer division on the right.

mod vs modulo (differ only for negative arguments)

Call Result Sign follows
mod(7, 3) / modulo(7, 3) 1 / 1 (agree when positive)
mod(-7, 3) -1 the dividend (like C %, Python math.fmod)
modulo(-7, 3) 2 the divisor (like Python %) — use for periodic wraparound

Intrinsic math functions

sqrt · abs · sin/cos/tan (radians!) · exp/log/log10 · ** · mod/modulo · max/min · real(x[,kind]) · aimag · int/nint/floor/ceiling · hypot. Degrees → radians: r = deg * pi / 180.0_dp. An intrinsic function is built in — no import, no library.

Named constants — parameter

real(dp), parameter :: pi = 3.141592653589793_dp   ! fixed at COMPILE time
real(dp), parameter :: two_pi = 2.0_dp * pi         ! may derive from other parameters

Reassigning a parameter is a compile-time error (a feature). Zero run-time cost — the value is folded into the code. Use it for dp, physical constants, and fixed sizes.

Formatted output (sketch — full story in Ch. 7)

Descriptor Prints Example → output
i0 / i4 integer (min width / width 4) i4 of 7␣␣␣7
f8.3 fixed-point, width 8, 3 decimals 21.5␣␣21.500
es12.4 scientific, mantissa in [1,10) 1.0e-4␣␣1.0000E-04
a character text as-is
1x / / one space / new line

A value too wide for its field prints as *s — a loud "widen me," not a silent truncation.

Numbers & rules worth memorizing

  • 1/2 == 0 — integer division truncates toward zero.
  • dp = selected_real_kind(15, 307); real(dp) everywhere; _dp on every real literal.
  • Trig takes radians. mod follows the dividend, modulo the divisor.
  • Default real ≈ 7 digits; real(dp) ≈ 15 digits. Default integer maxes near 2.1×10⁹.

🐍 Python contrasts (for the polyglot)

  • Python / is always real; Fortran integer / ≈ Python // but truncates (Fortran -7/2 = -3, Python -7//2 = -4).
  • Python ints are arbitrary-precision; Fortran integers are fixed-width (overflow near ±2.1×10⁹ by default).
  • Python floats are always 64-bit; Fortran default real is 32-bit — which is why we insist on real(dp).

Project piece added this chapter

kinds.f90 — a module kinds exporting integer, parameter :: dp = selected_real_kind(15, 307) — plus the plate's physical constants (alpha, length, nx, dx) as real(dp)/integer parameters, with dx derived safely via real(nx - 1, dp). This module is permanent; it reaches the Chapter 38 capstone unchanged.