What happens if one put NaN into min() or max()? Will it return NaN or the other number? Is it deterministic or random?
Sounds easy to test but its hard to implement. For example: How one get a quite-NaN number? A quite-NaN dosen't trigger debug traps, signaling-NaN do. There is no standard way in pre Fortran 2003! One have to google some nasty integer-float bit tricks. But with Fortran 2003 there is an intrinsic module ieee_arithmetic, which has the function ieee_value() which can return a quite-NaN. And one can test for Nan wich the function ieee_is_nan().
Remeber, C++ provide this functionality with std::numeric_limits::quite_nan() in C++1998 or earlier.
So the test ist quite simple. We put a number and NaN into min() and compare the result with our expected number. We do this for every combination and for min() and max(). The result for gfortran 6.3.2 32bit is: The returned number is alway not NaN, expect for the case where both parameter are NaN. Then, the function has no choice and returns NaN.
Back to the implementation. We want an automatic test, to quick start the program on different hardware and software settings. With a nice console output what happens, and what happens wrong.
There are two ways to do this. The easy one with lots of code duplication. And the right on, with only on test() function which get called for one test.
The test() function gets a varliable a, b which we wanna test. x als the expected result. A procedure pointer to either min() or max() and a short string with the function name we wanna test. For example:
call test(func, "min", 3.0, NAN, 3.0);
But... one can not get a procedure pointer on a intrinsic function!?? And if you implement your own function, it has to be a subroutine!??
Fortran is broken by design.
This ends up in lot more code and paint than is has to. See my next post where I implement this test in C++. Without pain and only half of code size.
Here is the 56line problem:
module test_m
use, intrinsic :: IEEE_ARITHMETIC
contains
subroutine test(func, funcname, a, b, x)
implicit none
procedure(), pointer :: func => null()
real, intent(in) :: a, b, x
character(len=*), intent(in) :: funcname
real :: res
call func(a,b,res)
write(*,*) funcname, "(",a, ",", b, ") =", res
if(ieee_is_nan(x)) then
if(.not. ieee_is_nan(res)) then
write(*,*) "But sould be", x
endif
else if(res /= x) then
write(*,*) "But should be", x
endif
end subroutine
subroutine mymin(a,b, x)
implicit none
real, intent(in) :: a, b
real, intent(out) :: x
x = min(a,b)
end subroutine
subroutine mymax(a,b, x)
implicit none
real, intent(in) :: a, b
real, intent(out) :: x
x = max(a,b)
end subroutine
end module
program fmin
use test_m
implicit none
real :: NAN
procedure(), pointer :: func => null()
NAN = IEEE_VALUE(NAN, IEEE_QUIET_NAN)
func => mymin
call test(func, "min", -3.0, NAN, -3.0)
call test(func, "min", NAN, -3.0, -3.0)
call test(func, "min", NAN, NAN, NAN)
func => mymax
call test(func, "max", -3.0, NAN, -3.0)
call test(func, "max", NAN, -3.0, -3.0)
call test(func, "max", NAN, NAN, NAN)
end program