Treating NaN as error: Helping debugging

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

and 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
1 Like

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

2 Likes

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.