I am writing a large hydrological model, and for some sets of parameters I get a NaN value, but I do not know which first function is giving me NaN. Therefore, I would like to stop my program at the first NaN.
A dirty debugging solution would be to test at every level isnan(…) which is tedious.
I am therefore wandering if there is an option in Julia to treat NaN as an error which will help to determine which function is causing the issue?
There is no option that tells Julia to treat
NaN as an error.
NaN arises from one of these (where the signs may be positive or negative):
Inf / Inf 0.0 / 0.0 Inf % x
inv(0.0) == Inf; x / 0.0 == Inf
Look at the routines to see where you may be dividing by zero (or introducing Inf).
chk(x) = (iszero(x) || isinf(x)) && error("x = $x") function chk(x,y) (iszero(x) || isinf(x)) && error("x = $x") (iszero(y) || isinf(y)) && error("y = $y") end function fn(a, b) chk(a, b) return a / b end
NaNs are valid
Float64s, engineered precisely for the purpose of making invalid results propagate without errors.
Your best option may be placing a few checks that validate outputs (or better, inputs). I find
isfinite useful for this purpose (it may catch a few things that turn to
NaNs later). I agree that it can be a bit cumbersome, but it can quickly pinpoint a problem.
Hmm, Fortran compilers let you enable floating-point exceptions, e.g.
gfortran -ffpe-trap=invalid will cause an exception once a NaN is created. I think technically this will cause a SIGFPE signal that can be caught by a signal handler. Would there be a way to do something similar in Julia, e.g. a library function to enable those exceptions and handling the signal will result in a backtrace?
Update: there’s an open issue on this, https://github.com/JuliaLang/julia/issues/27705
I wrote https://github.com/jwscook/ElideableMacros.jl as a way to try to understand macros (it turns out I didn’t master them). I’m fairly certain the hygiene is wrong, but it allowed me to create
@elideableassert, which can be used with
@elideableassert !isnan(x). The elision works by reading
ENV["ELIDE_ASSERTS"]. This is relatively close enabling something like gfortran’s compiler options as pointed out by @traktofon.
Use with caution! It’s buggy.