Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functions to return inf and nan of the specified type #37569

Closed
wants to merge 2 commits into from

Conversation

imciner2
Copy link
Contributor

This PR adds two new functions inf() and nan() that take a type and then will return the literal infinity or not a number of the specified type. These are useful when writing generic floating-point code, since the use of NaN or Inf will force the user of Float64. This can be seen in the following function:

function testfunc(x::T) where {T<:AbstractFloat}
    if x < 10.0
        x = NaN
    end

    return x
end

Which will give the following output:

julia> x = testfunc( Float64(1.0) )
NaN

julia> x = testfunc( Float32(1.0) )
NaN

Note that the return value is then automatically Float64 because the assignment to NaN in the if statement makes x into Float64 instead of keeping it as Float32 like it was passed in as (since NaN is simply an alias for the 64-bit NaN). While in this case you could simply force a return type of T on the function, if other processing is done before the return, then it is using the Float64 type. When prototyping algorithms, it is useful to test with different precisions to see how they behave, so this behavior makes that difficult.

This PR adds the functions inf() and nan(), so the sample function then can be:

function testfunc(x::T) where {T<:AbstractFloat}
    if x < 10.0
        x = nan(T)
    end

    return x
end

Which will give the following output:

julia> x = testfunc( Float64(1.0) )
NaN

julia> x = testfunc( Float32(1.0) )
NaN32

This now preserves the type of x during the assignment of NaN, so the entire contents of testfunc() can use x as any arbitrary floating-point type and not convert it.

@musm
Copy link
Contributor

musm commented Sep 14, 2020

The correct way to do this is to use

        x = T(NaN)

which accomplishes the same thing.

@DNF2
Copy link

DNF2 commented Sep 16, 2020

x = T(NaN)

That looks a bit awkward and inconsistent next to

x = zero(T)
y = one(T)

What is the rationale for zero and one that does not apply to nan and inf?

@musm
Copy link
Contributor

musm commented Sep 16, 2020

It's not about it looking awkward. Should we also add two(T), three(T), four(T) for consistency (rhetorical)? one and zero are special because they represent the additive and multiplicative identity.

@imciner2
Copy link
Contributor Author

I am proposing this because Infinity and not a number are also special values - we even have ways to test for them without using the standard equality check (e.g. isnan and isfinite/isinfinite). Another reason for this is it could allow easier creation of the infinity and not a number values for types - since you can specialize it instead of just calling convert like I do now, so you could actually do:

nan(::Type{Float64}) = NaN
nan(::Type{Float32}) = NaN32
nan(::Type{Float16}) = NaN16
etc...

Now, I decided not to do that in this code because I didn't see a large speed boost by specializing them completely and the code_typed output showed that the compiler already replaced the nan call with NaN32 in my above example when it compiled it (and I think it made it worse for the BigFloat types), but it can be done - and expanded for other types.

@musm
Copy link
Contributor

musm commented Sep 16, 2020

You don't see a speed boost, because there isn't one, as it stands it is redundant with T(NaN). The compiler is smart enough that this doesn't incur any penalty, and there is no disadvantage for defining convert methods, which is just as hard/easy as specializing the proposed nan function.

@stevengj
Copy link
Member

We used to have inf(T) and nan(T) and removed them in favor of T(Inf) and T(NaN) in Julia 0.4 (#8776).

@stevengj
Copy link
Member

stevengj commented Sep 16, 2020

What is the rationale for zero and one that does not apply to nan and inf?

Because zero and one don't have to be numbers at all — they are additive and multiplicative identities, respectively:

julia> using StaticArrays

julia> zero(SVector{3,Int})
3-element SArray{Tuple{3},Int64,1,3} with indices SOneTo(3):
 0
 0
 0

julia> one(SMatrix{3,3,Float64,9})
3×3 SArray{Tuple{3,3},Float64,2,9} with indices SOneTo(3)×SOneTo(3):
 1.0  0.0  0.0
 0.0  1.0  0.0
 0.0  0.0  1.0

or even

julia> one(String)
""

(since * for String is concatenation, the multiplicative identity is "").

In contrast, inf and nan only make sense for numeric types, where conversion works just fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants