Beginner

Operators in Fortran

Master arithmetic, relational, logical, and string operators in Fortran, including precedence rules and the language-specific power operator

Operators are the verbs of any programming language - they transform values into new values. Fortran, designed from the start to translate mathematical formulas into machine code, has a particularly rich and well-considered set of operators that closely mirror standard mathematical notation.

As a statically and strongly typed imperative language, Fortran enforces strict rules about which operators apply to which types. The compiler will catch attempts to add a string to an integer, but it will silently perform type promotion when mixing integers and reals in arithmetic - a behavior that has tripped up Fortran programmers since 1957.

This tutorial covers the four main operator categories in Fortran: arithmetic, relational (comparison), logical, and the string concatenation operator. We’ll also examine operator precedence and Fortran’s unique dual syntax for relational operators - a fascinating relic of its punch-card origins.

Arithmetic Operators

Fortran provides the standard arithmetic operators plus an exponentiation operator (**) that predates most other languages.

OperatorMeaningExample
+Additiona + b
-Subtraction (or unary negation)a - b, -a
*Multiplicationa * b
/Divisiona / b
**Exponentiation (power)a ** b

Create a file named arithmetic.f90:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
program arithmetic
    implicit none
    integer :: a, b
    real :: x, y, result

    a = 17
    b = 5
    x = 17.0
    y = 5.0

    ! Integer division truncates toward zero
    print *, "Integer 17 / 5 =", a / b
    print *, "Integer 17 mod 5 =", mod(a, b)

    ! Real division gives a true quotient
    print *, "Real 17.0 / 5.0 =", x / y

    ! Exponentiation with ** is a true operator, not a function call
    result = 2.0 ** 10
    print *, "2.0 ** 10 =", result

    ! Mixed mode promotes integer to real
    print *, "1 + 2.5 =", 1 + 2.5

    ! Negative exponent requires a real base
    print *, "2.0 ** (-3) =", 2.0 ** (-3)
end program arithmetic

Integer vs Real Division - A Classic Pitfall

When both operands of / are integers, Fortran performs integer division and truncates the result. 17 / 5 evaluates to 3, not 3.4. To get a real result, at least one operand must be real: 17.0 / 5 or real(17) / 5 both produce 3.4. The mod intrinsic returns the remainder.

Relational Operators

Fortran has two equivalent sets of relational operators. The modern symbolic form was added in Fortran 90; the older .lt. style dates to the original 1957 FORTRAN, when punch card hardware did not support < or > characters.

ModernClassicMeaning
==.eq.Equal to
/=.ne.Not equal to
<.lt.Less than
<=.le.Less than or equal to
>.gt.Greater than
>=.ge.Greater than or equal to

Create a file named relational.f90:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
program relational
    implicit none
    integer :: a, b
    logical :: result

    a = 10
    b = 20

    ! Modern symbolic form (Fortran 90+)
    print *, "10 == 20 :", a == b
    print *, "10 /= 20 :", a /= b
    print *, "10 <  20 :", a < b
    print *, "10 >= 20 :", a >= b

    ! Classic dotted form - still valid in modern Fortran
    print *, "10 .lt. 20 :", a .lt. b
    print *, "10 .eq. 20 :", a .eq. b

    ! Relational expressions produce logical values
    result = (a * 2) == b
    print *, "(10 * 2) == 20 :", result

    ! Beware: comparing reals for equality is rarely safe
    print *, "0.1 + 0.2 == 0.3 :", (0.1 + 0.2 == 0.3)
end program relational

Floating-Point Equality

The last line will print F (false) because 0.1 + 0.2 does not equal 0.3 exactly in binary floating point. For real number comparison, always test that the absolute difference is below a small tolerance: abs(x - y) < 1.0e-6.

Logical Operators

Logical operators in Fortran always use the dotted form - there is no symbolic equivalent like && or ||. They operate on values of type logical.

OperatorMeaning
.not.Logical negation
.and.Logical AND
.or.Logical OR
.eqv.Logical equivalence (XNOR)
.neqv.Logical non-equivalence (XOR)

Create a file named logical_ops.f90:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
program logical_ops
    implicit none
    logical :: t, f
    integer :: age, score

    t = .true.
    f = .false.

    print *, ".not. true       :", .not. t
    print *, "true  .and. false:", t .and. f
    print *, "true  .or.  false:", t .or. f
    print *, "true  .eqv. true :", t .eqv. t
    print *, "true  .neqv. false:", t .neqv. f

    ! Combining relational and logical operators
    age = 25
    score = 85
    print *, "Eligible:", (age >= 18) .and. (score > 70)

    ! Fortran does NOT guarantee short-circuit evaluation
    ! Both sides of .and. and .or. may be evaluated
end program logical_ops

No Short-Circuit Evaluation

Unlike C or Python, Fortran does not guarantee that the second operand of .and. or .or. will be skipped when the result is already determined. The compiler may evaluate both sides. Never rely on the left side of .and. to guard a potentially unsafe right side - use a nested if instead.

String Concatenation

Fortran uses // (double slash) for string concatenation. This is one of the few language-specific operators that often surprises programmers from other backgrounds.

Create a file named string_ops.f90:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
program string_ops
    implicit none
    character(len=20) :: first, last
    character(len=50) :: full

    first = "Grace"
    last  = "Hopper"

    ! Concatenation with // - note that fixed-length strings are padded with spaces
    full = trim(first) // " " // trim(last)
    print *, "Full name: [", trim(full), "]"

    ! Without trim, you get the padding spaces too
    full = first // " " // last
    print *, "Untrimmed: [", full, "]"

    ! String comparison uses the same relational operators
    print *, "'abc' < 'abd' :", "abc" < "abd"
end program string_ops

Operator Precedence

Fortran’s precedence rules follow standard mathematical conventions, with one notable feature: exponentiation is right-associative, so 2 ** 3 ** 2 evaluates as 2 ** (3 ** 2) = 2 ** 9 = 512.

From highest to lowest precedence:

  1. ** (exponentiation, right-associative)
  2. *, /
  3. Unary +, -
  4. Binary +, -
  5. // (string concatenation)
  6. Relational operators (==, /=, <, etc.)
  7. .not.
  8. .and.
  9. .or.
  10. .eqv., .neqv.

Create a file named precedence.f90:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
program precedence
    implicit none
    integer :: result

    ! Right-associative exponentiation
    result = 2 ** 3 ** 2
    print *, "2 ** 3 ** 2 =", result   ! 512, not 64

    ! Standard precedence: * before +
    print *, "2 + 3 * 4 =", 2 + 3 * 4   ! 14, not 20

    ! Parentheses force evaluation order
    print *, "(2 + 3) * 4 =", (2 + 3) * 4   ! 20

    ! Mixing relational and logical
    print *, "1 < 2 .and. 3 > 2 :", 1 < 2 .and. 3 > 2

    ! Unary minus has lower precedence than **
    print *, "-2 ** 2 =", -2 ** 2   ! -4, parsed as -(2**2)
end program precedence

Running with Docker

1
2
3
4
5
6
7
8
9
# Pull the official GCC image (includes gfortran)
docker pull gcc:latest

# Compile and run each example
docker run --rm -v $(pwd):/app -w /app gcc:latest sh -c 'gfortran -o arithmetic arithmetic.f90 && ./arithmetic'
docker run --rm -v $(pwd):/app -w /app gcc:latest sh -c 'gfortran -o relational relational.f90 && ./relational'
docker run --rm -v $(pwd):/app -w /app gcc:latest sh -c 'gfortran -o logical_ops logical_ops.f90 && ./logical_ops'
docker run --rm -v $(pwd):/app -w /app gcc:latest sh -c 'gfortran -o string_ops string_ops.f90 && ./string_ops'
docker run --rm -v $(pwd):/app -w /app gcc:latest sh -c 'gfortran -o precedence precedence.f90 && ./precedence'

Expected Output

Output from arithmetic.f90:

 Integer 17 / 5 =           3
 Integer 17 mod 5 =           2
 Real 17.0 / 5.0 =   3.40000010    
 2.0 ** 10 =   1024.00000    
 1 + 2.5 =   3.50000000    
 2.0 ** (-3) =  0.125000000    

Output from relational.f90:

 10 == 20 : F
 10 /= 20 : T
 10 <  20 : T
 10 >= 20 : F
 10 .lt. 20 : T
 10 .eq. 20 : F
 (10 * 2) == 20 : T
 0.1 + 0.2 == 0.3 : F

Output from logical_ops.f90:

 .not. true       : F
 true  .and. false: F
 true  .or.  false: T
 true  .eqv. true : T
 true  .neqv. false: T
 Eligible: T

Output from string_ops.f90:

 Full name: [Grace Hopper]
 Untrimmed: [Grace               Hopper              ]
 'abc' < 'abd' : T

Output from precedence.f90:

 2 ** 3 ** 2 =         512
 2 + 3 * 4 =          14
 (2 + 3) * 4 =          20
 1 < 2 .and. 3 > 2 : T
 -2 ** 2 =          -4

Key Concepts

  • Integer division truncates - 17 / 5 is 3; promote at least one operand to real for true division
  • Two relational syntaxes coexist - == and .eq. mean the same thing; the dotted form is a holdover from punch-card hardware
  • Logical operators are always dotted - Fortran uses .and., .or., .not. - never && or ||
  • No short-circuit evaluation - The compiler may evaluate both operands of .and. and .or.; never use them as guards
  • ** is exponentiation and right-associative - 2 ** 3 ** 2 is 512, and -2 ** 2 is -4 (the ** binds tighter than unary minus)
  • // concatenates strings - Fixed-length character variables are space-padded; use trim() to remove trailing spaces
  • Mixed-mode arithmetic promotes - An integer combined with a real is converted to real automatically; the assigned variable’s type may still cause truncation
  • Never test reals with == - Compare the absolute difference against a tolerance instead

Running Today

All examples can be run using Docker:

docker pull gcc:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining