rdb_safe_math Module

Single point of control the math functions that break bitwise reproducibility across compilers, GPU vendors, and optimization levels would route through IF adopted: exp, log, sin, cos, sqrt, pow.

Status (PR-8): the safe_* wrappers (safe_exp, safe_log, safe_sin, safe_cos, safe_sqrt, safe_pow) are plain elemental inlines around the bare intrinsic — zero overhead, codegen identical to writing exp(x) directly. This module used to carry a second “safe” mode, selected by the RDB_BITWISE_REPRO build option, that dispatched the wrappers to the *_polynomial implementations below instead. That option was removed in PR-8: grep across src//app/ found zero call sites of any safe_* wrapper from a production kernel (one stale comment, no calls) — the option changed nothing at all, so it was a lie about a capability nothing used.

What SURVIVES this PR: every *_polynomial function and its coefficients are correct, unit-tested (tests/test_safe_math.F90, unconditionally compiled — no #ifdef), and are the raw material for a future bitwise-reproducibility PR. This module is a library with no production consumer today — adopting safe_* in kernels (which would first require re-adding a build-time or run-time dispatch) is future work, not a promise this module currently keeps.

Would-be convention (not currently enforced by any build path): a kernel wanting repro-safety would call the safe_* wrapper instead of the bare intrinsic. For integer exponents, a**2 / a**3 etc. compile to multiplications and are already deterministic — don’t wrap those.

Coverage: safe_exp, safe_log, safe_sin, safe_cos each have real polynomial paths (Cody-Waite reduction + Horner Taylor). safe_sqrt is IEEE-correctly-rounded by mandate so the intrinsic path is already deterministic. safe_pow’s polynomial path is unused by safe_pow itself (which is the bare intrinsic **) — it exists for a future safe_exp(b * safe_log(a)) decomposition.


Uses

  • module~~rdb_safe_math~~UsesGraph module~rdb_safe_math rdb_safe_math iso_fortran_env iso_fortran_env module~rdb_safe_math->iso_fortran_env module~rdb_constants rdb_constants module~rdb_safe_math->module~rdb_constants pic_types pic_types module~rdb_constants->pic_types

Variables

Type Visibility Attributes Name Initial
real(kind=wp), private, parameter :: COS_C0 = 1.0_wp
real(kind=wp), private, parameter :: COS_C1 = -1.0_wp/2.0_wp
real(kind=wp), private, parameter :: COS_C2 = 1.0_wp/24.0_wp
real(kind=wp), private, parameter :: COS_C3 = -1.0_wp/720.0_wp
real(kind=wp), private, parameter :: COS_C4 = 1.0_wp/40320.0_wp
real(kind=wp), private, parameter :: COS_C5 = -1.0_wp/3628800.0_wp
real(kind=wp), private, parameter :: COS_C6 = 1.0_wp/479001600.0_wp
real(kind=wp), private, parameter :: COS_C7 = -1.0_wp/87178291200.0_wp
real(kind=wp), private, parameter :: COS_C8 = 1.0_wp/20922789888000.0_wp
real(kind=wp), private, parameter :: EXP_C0 = 1.0_wp
real(kind=wp), private, parameter :: EXP_C1 = 1.0_wp
real(kind=wp), private, parameter :: EXP_C10 = 1.0_wp/3628800.0_wp
real(kind=wp), private, parameter :: EXP_C11 = 1.0_wp/39916800.0_wp
real(kind=wp), private, parameter :: EXP_C12 = 1.0_wp/479001600.0_wp
real(kind=wp), private, parameter :: EXP_C13 = 1.0_wp/6227020800.0_wp
real(kind=wp), private, parameter :: EXP_C2 = 1.0_wp/2.0_wp
real(kind=wp), private, parameter :: EXP_C3 = 1.0_wp/6.0_wp
real(kind=wp), private, parameter :: EXP_C4 = 1.0_wp/24.0_wp
real(kind=wp), private, parameter :: EXP_C5 = 1.0_wp/120.0_wp
real(kind=wp), private, parameter :: EXP_C6 = 1.0_wp/720.0_wp
real(kind=wp), private, parameter :: EXP_C7 = 1.0_wp/5040.0_wp
real(kind=wp), private, parameter :: EXP_C8 = 1.0_wp/40320.0_wp
real(kind=wp), private, parameter :: EXP_C9 = 1.0_wp/362880.0_wp
real(kind=wp), private, parameter :: EXP_OVERFLOW = log(huge(1.0_wp))
real(kind=wp), private, parameter :: EXP_UNDERFLOW = log(tiny(1.0_wp))
real(kind=wp), private, parameter :: INV_LN2 = 1.4426950408889634074_wp
real(kind=wp), private, parameter :: INV_PI_OVER_2 = 0.6366197723675813431_wp
real(kind=wp), private, parameter :: LN2_HI = 0.6931471805599452862_wp
real(kind=wp), private, parameter :: LN2_LO = 2.319046813846299558e-17_wp
real(kind=wp), private, parameter :: LOG_C0 = 1.0_wp
real(kind=wp), private, parameter :: LOG_C1 = 1.0_wp/3.0_wp
real(kind=wp), private, parameter :: LOG_C2 = 1.0_wp/5.0_wp
real(kind=wp), private, parameter :: LOG_C3 = 1.0_wp/7.0_wp
real(kind=wp), private, parameter :: LOG_C4 = 1.0_wp/9.0_wp
real(kind=wp), private, parameter :: LOG_C5 = 1.0_wp/11.0_wp
real(kind=wp), private, parameter :: LOG_C6 = 1.0_wp/13.0_wp
real(kind=wp), private, parameter :: LOG_C7 = 1.0_wp/15.0_wp
real(kind=wp), private, parameter :: LOG_C8 = 1.0_wp/17.0_wp
real(kind=wp), private, parameter :: LOG_C9 = 1.0_wp/19.0_wp
real(kind=wp), private, parameter :: PI_OVER_2_HI = 1.5707963267948965580_wp
real(kind=wp), private, parameter :: PI_OVER_2_LO = 6.123233995736766036e-17_wp
real(kind=wp), private, parameter :: SIN_C0 = 1.0_wp
real(kind=wp), private, parameter :: SIN_C1 = -1.0_wp/6.0_wp
real(kind=wp), private, parameter :: SIN_C2 = 1.0_wp/120.0_wp
real(kind=wp), private, parameter :: SIN_C3 = -1.0_wp/5040.0_wp
real(kind=wp), private, parameter :: SIN_C4 = 1.0_wp/362880.0_wp
real(kind=wp), private, parameter :: SIN_C5 = -1.0_wp/39916800.0_wp
real(kind=wp), private, parameter :: SIN_C6 = 1.0_wp/6227020800.0_wp
real(kind=wp), private, parameter :: SIN_C7 = -1.0_wp/1307674368000.0_wp
real(kind=wp), private, parameter :: SIN_C8 = 1.0_wp/355687428096000.0_wp
real(kind=wp), private, parameter :: SQRT_HALF = 0.7071067811865475244_wp

Functions

public elemental function safe_cos(x) result(y)

cos(x). Plain elemental inline around the intrinsic; see safe_exp and the module docstring.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: x

Return Value real(kind=wp)

public elemental function safe_cos_polynomial(x) result(y)

Public only for the unit-test suite (no production module imports it); ignore when developing production code in other modules. cos(x) via the same Cody-Waite reduction; the quadrant map is shifted by 1 vs sin:

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: x

Return Value real(kind=wp)

public elemental function safe_exp(x) result(y)

exp(x). Plain elemental inline around the intrinsic — zero overhead, codegen identical to writing exp(x) directly. No production module calls this today (PR-8 removed the RDB_BITWISE_REPRO dispatch that once routed it to safe_exp_polynomial, which enabled nothing); see the module docstring.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: x

Return Value real(kind=wp)

public elemental function safe_exp_polynomial(x) result(y)

Public only for the unit-test suite (no production module imports it); ignore when developing production code in other modules. Cody-Waite range reduction x = k*ln(2) + r, |r| ≤ ln(2)/2, followed by a 14-term Horner Taylor on exp(r) and an IEEE-exact 2^k multiply via scale(). Max relative error ≈ 2-3 ULP across [-709, 709].

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: x

Return Value real(kind=wp)

public elemental function safe_log(x) result(y)

log(x) (natural log). Plain elemental inline around the intrinsic; see safe_exp and the module docstring.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: x

Return Value real(kind=wp)

public elemental function safe_log_polynomial(x) result(y)

Public only for the unit-test suite (no production module imports it); ignore when developing production code in other modules. log(x) via IEEE exponent extraction + atanh-form series.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: x

Return Value real(kind=wp)

public elemental function safe_pow(base, exponent) result(y)

Non-integer exponent only. For integer exponents (a**2, a**3, …), write the multiplication out — the compiler unrolls those and they’re already deterministic. Plain elemental inline around the intrinsic; see safe_exp and the module docstring.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: base
real(kind=wp), intent(in) :: exponent

Return Value real(kind=wp)

public elemental function safe_pow_polynomial(base, exponent) result(y)

Public only for the unit-test suite (no production module imports it); ignore when developing production code in other modules. base^exponent via the identity base^exponent = exp(exponent * log(base)) Routes through the polynomial log and exp paths so the result is deterministic across builds.

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: base
real(kind=wp), intent(in) :: exponent

Return Value real(kind=wp)

public elemental function safe_sin(x) result(y)

sin(x). Plain elemental inline around the intrinsic; see safe_exp and the module docstring.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: x

Return Value real(kind=wp)

public elemental function safe_sin_polynomial(x) result(y)

Public only for the unit-test suite (no production module imports it); ignore when developing production code in other modules. sin(x) via Cody-Waite reduction mod π/2 and a 9-term Horner Taylor on the reduced argument. The quadrant k mod 4 picks sin vs cos and a sign:

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: x

Return Value real(kind=wp)

public elemental function safe_sqrt(x) result(y)

IEEE-correctly-rounded by mandate, so the intrinsic is already bit-identical across compliant compilers.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: x

Return Value real(kind=wp)

private elemental function cos_reduced(r) result(y)

Horner cos Taylor on |r| ≤ π/4. 9 even-power terms.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: r

Return Value real(kind=wp)

private elemental function sin_reduced(r) result(y)

Horner sin Taylor on |r| ≤ π/4. sin(r) = r * P(r²) where P is the odd-power polynomial in r² — avoids computing r¹, r³, r⁵… separately. 9 terms (highest power r¹⁷) gives < 1e-18 truncation error.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: r

Return Value real(kind=wp)