Skip to main content
GitHub

TYP04-F. Centralize kind definitions to ensure consistent precision

Programmers should define numeric kind parameters in a centralized module using standard-compliant mechanisms, such as selected_real_kind or iso_fortran_env , to ensure consistent and portable precision throughout the codebase.

In Fortran, the precision and range of intrinsic numeric types ( integer , real , complex ) are determined by their kind type parameter. Relying on default types (e.g., real ) is hazardous because their actual precision is unspecified and may vary depending on the compiler or compiler flags.

Legacy extensions, such as real*4 or real*8, are not standard and reduce code portability. Similarly, hard-coding kind values (e.g., kind=8 ) is discouraged, as compilers may interpret them differently, either as byte sizes or as sequential kind identifiers.

To ensure consistent and portable precision, developers should define numeric kinds using standard inquiry functions, such as selected_real_kind or constants provided by the iso_fortran_env module. Centralizing these definitions in a module allows changes to precision requirements to propagate throughout the codebase by modifying a single line, improving reliability and maintainability.

Noncompliant Code Example

This code mixes default real , legacy double precision , and non-standard real*8 . The precision of x and y is compiler-dependent, while z relies on a non-standard extension, reducing portability and introducing potential numerical inconsistencies.

Non-compliant code
program test_inconsistent_precision
  implicit none

  ! Noncompliant: Default REAL depends on compiler defaults
  real :: x
  ! Noncompliant: DOUBLE PRECISION is legacy and varies by compiler
  double precision :: y
  ! Noncompliant: REAL*8 is a non-standard extension
  real*8 :: z

  x = 0.123456
  y = 0.123456789012345d0
  z = 1.0e0

  print *, x, y, z
end program

Compliant Solution

By defining kinds centrally using selected_real_kind , this solution ensures consistent precision across the codebase and abstracts away compiler-specific defaults. Updating precision later requires modifying only the module.

Compliant code
module precision_mod
  implicit none
  public

  ! Compliant: sp defines 6 significant digits, range of 10^-37 to 10^37
  integer, parameter :: sp = selected_real_kind(6, 37)
  ! Compliant: dp defines 15 significant digits, range of 10^307 to 10^307
  integer, parameter :: dp = selected_real_kind(15, 307)
end module precision_mod

program test_precision
  use precision_mod, only: sp, dp
  implicit none

  real(kind=sp) :: x
  real(kind=dp) :: y
  real(kind=dp) :: z

  x = 0.123456_sp
  y = 0.123456789012345_dp
  z = 1.0_dp

  print *, x, y
end program

Compliant Solution

For applications requiring specific storage sizes, iso_fortran_env provides consistent bit-width kinds (e.g., 32-bit and 64-bit reals), ensuring portability across platforms and compatibility with binary formats or C interoperability.

Compliant code
module precision_mod
  use iso_fortran_env, only: real32, real64
  implicit none
  public

  integer, parameter :: f32 = real32
  integer, parameter :: f64 = real64
end module precision_mod

program test_precision
  use precision_mod, only: f32, f64
  implicit none

  real(kind=f32) :: val_a 
  real(kind=f64) :: val_b

  val_a = 1.0_f32
  val_b = 1.0_f64
end program

Risk Assessment

Not centralizing and explicitly defining numeric kinds can lead to precision drift, where different components of an application use inconsistent floating-point widths. This may cause unexpected numerical results, compromise data integrity, and create portability issues across compilers or hardware platforms.

RecommendationSeverityLikelihoodDetectableRepairablePriorityLevel
TYP04-FHighUnlikelyYesNoP6L2

Bibliography

[ Fortran 2023 Interpretation Document ]Section 16.10.2.27

Attachments: