Numerical Stability
Bounded Divide
Guarding Small Denominators
A defensive numeric calculation can replace a tiny denominator before division.
Program
Play the program to choose a denominator and see whether the guard is used.
bounded_divide.f90
Replay: real traced execution (multi-file project)
program bounded_divide_demo
implicit none
real :: numerator
real :: raw_denominator
real :: denominator
real :: quotient
integer :: used_floor
numerator = 12.0
raw_denominator = 0.0
denominator = raw_denominator
used_floor = 0
if (abs(denominator) < 1.0) then
denominator = 1.0
used_floor = 1
end if
quotient = numerator / denominator
print '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotient
end program bounded_divide_demo
program bounded_divide_demo
implicit none
real :: numerator
real :: raw_denominator
real :: denominator
real :: quotient
integer :: used_floor
numerator = 12.0
raw_denominator = 2.0
denominator = raw_denominator
used_floor = 0
if (abs(denominator) < 1.0) then
denominator = 1.0
used_floor = 1
end if
quotient = numerator / denominator
print '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotient
end program bounded_divide_demo
program bounded_divide_demo
implicit none
real :: numerator
real :: raw_denominator
real :: denominator
real :: quotient
integer :: used_floor
numerator = 12.0
raw_denominator = 6.0
denominator = raw_denominator
used_floor = 0
if (abs(denominator) < 1.0) then
denominator = 1.0
used_floor = 1
end if
quotient = numerator / denominator
print '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotient
end program bounded_divide_demo
numerator ← 12.0
9numerator = 12.010raw_denominator = 0.0values this step12.0numeratorraw_denominator ← 0.0
9numerator = 12.010raw_denominator = 0.011denominator = raw_denominatorvalues this step0.0raw_denominatordenominator ← 0.0
10raw_denominator = 0.011denominator = raw_denominator12used_floor = 0values this step0.0denominatorused_floor ← 0
11denominator = raw_denominator12used_floor = 013if (abs(denominator) < 1.0) thenvalues this step0used_floorif (abs(denominator) < 1.0) then
12used_floor = 013if (abs(denominator) < 1.0) then14 denominator = 1.0values this step.true.abs(denominator) < 1.0denominator ← 1.0
13if (abs(denominator) < 1.0) then14 denominator = 1.015 used_floor = 1values this step1.0denominatorused_floor ← 1
14 denominator = 1.015 used_floor = 116end ifvalues this step1used_floorquotient ← 12.0
16end if17quotient = numerator / denominator18print '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotientvalues this step12.0quotient12.0numerator1.0denominatorprint '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotien…
17 quotient = numerator / denominator18 print '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotient19end program bounded_divide_demooutput0.0 1 12.0values this step0.0raw_denominator1used_floor12.0quotient
numerator ← 12.0
9numerator = 12.010raw_denominator = 2.0values this step12.0numeratorraw_denominator ← 2.0
9numerator = 12.010raw_denominator = 2.011denominator = raw_denominatorvalues this step2.0raw_denominatordenominator ← 2.0
10raw_denominator = 2.011denominator = raw_denominator12used_floor = 0values this step2.0denominatorused_floor ← 0
11denominator = raw_denominator12used_floor = 013if (abs(denominator) < 1.0) thenvalues this step0used_floorif (abs(denominator) < 1.0) then
12used_floor = 013if (abs(denominator) < 1.0) then14 denominator = 1.0values this step.false.abs(denominator) < 1.0quotient ← 6.0
16end if17quotient = numerator / denominator18print '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotientvalues this step6.0quotient12.0numerator2.0denominatorprint '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotien…
17 quotient = numerator / denominator18 print '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotient19end program bounded_divide_demooutput2.0 0 6.0values this step2.0raw_denominator0used_floor6.0quotient
numerator ← 12.0
9numerator = 12.010raw_denominator = 6.0values this step12.0numeratorraw_denominator ← 6.0
9numerator = 12.010raw_denominator = 6.011denominator = raw_denominatorvalues this step6.0raw_denominatordenominator ← 6.0
10raw_denominator = 6.011denominator = raw_denominator12used_floor = 0values this step6.0denominatorused_floor ← 0
11denominator = raw_denominator12used_floor = 013if (abs(denominator) < 1.0) thenvalues this step0used_floorif (abs(denominator) < 1.0) then
12used_floor = 013if (abs(denominator) < 1.0) then14 denominator = 1.0values this step.false.abs(denominator) < 1.0quotient ← 2.0
16end if17quotient = numerator / denominator18print '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotientvalues this step2.0quotient12.0numerator6.0denominatorprint '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotien…
17 quotient = numerator / denominator18 print '(F0.1, 1X, I0, 1X, F0.1)', raw_denominator, used_floor, quotient19end program bounded_divide_demooutput6.0 0 2.0values this step6.0raw_denominator0used_floor2.0quotient
guard
`abs(denominator) < 1.0` detects a risky denominator.
floor
The denominator is replaced with `1.0` only when the guard fires.
audit flag
`used_floor` records whether the stabilized path was used.