Return NaN instead of DomainError


#1

I could not find a way to make function calls like sqrt(-2) return NaN instead of raising an error. Is that possible?

It would be quite useful, at least sometimes, to just let the NaN propagate to the final result. Also this would save a comparison with zero which is always present is sqrt code now.


#2

#3

If your input are floats, then you can use @fastmath, but it will not work with other types:

julia> @fastmath sqrt(-1.0)
NaN

julia> @fastmath sqrt(-1)
ERROR: DomainError with -1.0:
sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
Stacktrace:
 [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31
 [2] sqrt at ./math.jl:479 [inlined]
 [3] sqrt at ./math.jl:505 [inlined]
 [4] sqrt_fast(::Int64) at ./fastmath.jl:361
 [5] top-level scope at none:0

julia> @fastmath sqrt(-1//1)
ERROR: DomainError with -1.0:
sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
Stacktrace:
 [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31
 [2] sqrt at ./math.jl:479 [inlined]
 [3] sqrt at ./math.jl:505 [inlined]
 [4] sqrt_fast(::Rational{Int64}) at ./fastmath.jl:361
 [5] top-level scope at none:0

#4

Wow, I would never guess that @fastmath changes the visible behaviour that much. At least the docs give impression that it only enable less precision.

Edit: the docs indeed contain the info about NaNs, a few pages below.


#5
julia> isnan(x) = @fastmath isnan(x)
isnan (generic function with 1 method)

julia> isnan(NaN)
false

#6

No. Don’t do that!!! It’s undefined behavior when you have NaN in @fastmath.


#7

The package looks like it does what’s needed. And even through they wrap the Base.sqrt function, the compiler probably is smart enough not to perform the comparison twice. And by the way, their trig functions differ from Base ones not only in NaN handling: they just use libm functions, while Base has Julia implementations. Not sure why the case for sqrt is different.


#8

Good to know that.
Just to be sure I understand you correctly: you are saying that using NaN values as input ta a @fastmath function will lead to undefined behavior, right? Not that @fastmath sqrt(-1.0) leads to undefined behavior, correct?


#9

No I’m saying that what you suggested is undefined behavior, i.e. @fastmath sqrt(-1.0) is undefined behavior. @fastmath assumes NaN doesn’t exist anywhere. It’s a mere implementation detail that @fastmath sqrt(-1.0) return NaN. It could throw an error or even crash.


#10

The suggested solution with NaNMath package is ok and works, but:
Is there any way to make all math functions return NaN in a large block of code? Like I have a block like this:

for i in ...:
    a[i] = sqrt(f(i))
    ...
    if ...:
        arr = [sqrt(f(j)) for j in ...]
        b[i] = log(f(arr))
    ...
end

And would really like all functions like sqrt and log here to just produce nans. The only way I see is manually find offending function and replace them with NaNMath implementations. Oh, and what to do if f(...) - some external function - also contains sqrt and others, and thus throws domainerror instead of just returning nan? And if I forget some function, which just happens not to throw when run on test data, it will explode at some unexpected time later.

Note that in this example it’s not easily possible to put catches in relevant locations. Even for simpler examples, this looks far from an expected quick solution…