# min(NaN,5) = NaN (why no @fastmath?)

**URL:** <https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736>\
**Category:** General Usage\
**Tags:** nan\
**Created:** [January 29, 2023, 5:07pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736 "2023-01-29T17:07:14Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [February 1, 2023, 1:29pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/21 "2023-02-01T13:29:12Z")

</div>

Sure, but it allocates the filtered array - unlike `skip`. Also, `skip` can be used with functions like `findmax` - unlike `filter`.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [February 1, 2023, 3:09pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/22 "2023-02-01T15:09:14Z")

</div>

Thank you. What do you think of these package-free alternatives that do not allocate:

```julia
maximum(u for u in x if !isnan(u))
extrema(u for u in x if !isnan(u))
findmax(ifelse(isnan(u),-Inf,u) for u in x)

```

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [February 1, 2023, 3:30pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/23 "2023-02-01T15:30:06Z")

</div>

> [@rafael.guerra](#):
>
> ```julia
> findmax(ifelse(isnan(u),-Inf,u) for u in x)
> 
> ```

A few drawbacks:  
Only works for “plain” numbers, eg not `Unitful` units.  
Only type-stable for Float64, eg not for Float32.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [February 1, 2023, 3:34pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/24 "2023-02-01T15:34:30Z")

</div>

Thanks, for the type stability this helps:

```julia
findmax(ifelse(isnan(u),typemin(eltype(x)),u) for u in x) 

```

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [February 1, 2023, 3:46pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/25 "2023-02-01T15:46:25Z")

</div>

Yes, it does.  
Also, when all values are NaNs and the result is undefined, this would return -infinity and silently continue, potentially leading to incorrect results further downstream. Meanwhile, `skip + findmax` would correctly throw an error.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [February 1, 2023, 4:14pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/27 "2023-02-01T16:14:23Z")

</div>

Ok, this non-allocating function should be more robust:

```julia
function findmax_nan(x)
    all(isnan, x) && throw("Error: all NaN!")
    i = findfirst(!isnan, x)
    m, j = findmax(ifelse(isnan(u), typemin(eltype(x)), u) for u in x)
    return m, max(i,j)
end

```

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [February 1, 2023, 6:56pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/28 "2023-02-01T18:56:48Z")

</div>

> [@StefanKarpinski](#):
>
> Under the “not a real” interpretation of NaN there’s never any justification of not propagating a NaN since it’s a permanent poison value.

The plot thickens, that doesn’t apply for qNaN as “missing data” vs sNaN “propagate”.

See Table I. in the given link, I think we want to support what (some) standard does (most do not propagate, at least always, though Java does according to that doc), and preferentially what hardware does (I’m guessing these are most performant instructions), e.g. “Intel® AVX-512 VRAGESS”, “ARM® ARMv8.2 FMINNM / FMAXNM”, conformig to this (C) standard:

> 3.2 TS18661-1: sNaN Propagate but qNaN Are Missing Data […]

> As a result, fmax will have the following new behavior:  
> fmax (1, qNaN) → 1 (5)  
> fmax (1, sNaN) → qNaN (6)

Seems in compliance with this standard (or is strengthening it):

> 3.1 C11: qNaN Are Missing Data […]

> fmax (1, qNaN) → 1 (3)  
> fmax (1, sNaN) → undefined (4)

> **[ISO/IEC TS 18661-1:2014](https://www.iso.org/standard/63146.html)**
>
> Information technology — Programming languages, their environments, and system software interfaces — Floating-point extensions for C — Part 1: Binary floating-point arithmetic

> This standard was last reviewed and confirmed in 2022. Therefore this version remains current.
> 
> ### Abstract
> 
> ISO/IEC TS 18661-1:2014 extends programming language C to support binary floating-point arithmetic conforming to ISO/IEC/IEEE 60559:2011

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [February 1, 2023, 8:45pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/29 "2023-02-01T20:45:31Z")

</div>

> [@rafael.guerra](#):
>
> Ok, this non-allocating function should be more robust:
> 
> ```julia
> function findmax_nan(x)
> 
> ```

Yeah, one can write individual functions like this, and even properly take care of all types and edgecases. Or just use nicely composable `skip` with regular aggregations (:  
It’s like `Base.skipmissing`, but for all you nothings, nans, negatives, etc.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [February 1, 2023, 8:55pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/30 "2023-02-01T20:55:00Z")

</div>

This feels simultaneously more ergonomic and more general/composable:

```julia
maximum(skip(isnan, A))

```

also allows you to write things like

```julia
findmax(skip(isnan, A))
sum(skip(isodd, B))
prod(skip(iszero, C))

```

It’d be nice if this was in Base.

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [February 1, 2023, 10:08pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/31 "2023-02-01T22:08:36Z")

</div>

> [@uniment](#):
>
> It’d be nice if this was in Base.

See `Iterators.filter`. For example,

```julia-repl
julia> x = collect(Float64,1:10); x[3] = NaN;

julia> maximum(Iterators.filter(!isnan,x))
10.0

```

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [February 1, 2023, 10:10pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/32 "2023-02-01T22:10:49Z")

</div>

> [@mikmoore](#):
>
> `maximum(Iterators.filter(!isnan,x))`

That works ok, until you try this:

```julia
julia> findmax(Iterators.filter(!isnan,x))
ERROR: MethodError: no method matching keys(::Base.Iterators.Filter{ComposedFunction{typeof(!), typeof(isnan)}, Vector{Float64}})

```

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [February 1, 2023, 10:23pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/33 "2023-02-01T22:23:47Z")

</div>

I guess I consider that a weird case. Should it return the index into the original `x` or the index of the `x` with the skips removed? I’m not sure this generalizes very well. One can use `Base.filter` for the skips-removed case. For the skips-retained case, I guess one could use a monstrosity like

```julia-repl
julia> argmax(first,Iterators.filter(!isnan∘first,zip(x,keys(x))))
(10.0, 10)

```

but I’ll admit the ergonomics leave something to be desired.

EDIT: maybe slightly nicer as

```julia-repl
julia> argmax(last,Iterators.filter(!isnan∘last,pairs(x)))
10 => 10.0

```

but ultimately the same.

---

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [February 1, 2023, 10:32pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/34 "2023-02-01T22:32:51Z")

</div>

> [@mikmoore](#):
>
> Should it return the index into the original `x` or the index of the `x` with the skips removed? I’m not sure this generalizes very well.

Well, I think “`Base.filter` should be fungible with `Iterators.filter` when possible” generalizes pretty well

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [February 1, 2023, 10:35pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/35 "2023-02-01T22:35:14Z")

</div>

That’s where `skip` brings benefits: like `skipmissing`, it’s understood to return just a view with the original `x`’s indexing retained but iterating skips over certain elements. This is unlike `filter`, which is understood to give a new collection altogether, even if `Iterators.filter` does so lazily.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [February 1, 2023, 11:08pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/36 "2023-02-01T23:08:48Z")

</div>

> [@uniment](#):
>
> This feels simultaneously more ergonomic and more general/composable:

We have the freedom to choose according to the application.  
Fyi, some benchmarking:

```julia
x = rand(Float32[NaN,1,2,3.0], 1_000_000)
@btime findmax_nan($x) # 1.344 ms (0 allocs: 0 bytes)
@btime argmax(first, Iterators.filter(!isnan∘first, zip($x, keys($x)))) # 3.391 ms (0 allocs: 0 bytes)
@btime findmax(skip(isnan, $x)) # 8.464 ms (0 allocs: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [February 1, 2023, 11:17pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/37 "2023-02-01T23:17:11Z")

</div>

~~Looks like Skipper.jl has some room for performance improvement @aplavin 👀~~

~~@rafael.guerra do you know how much of the performance differential is due to [this](https://discourse.julialang.org/t/range-statistics/92518/24)?~~

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [February 1, 2023, 11:19pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/38 "2023-02-01T23:19:04Z")

</div>

> [@rafael.guerra](#):
>
> Fyi, some benchmarking:

Looks different to me, latest stable Julia 1.8:

```julia
julia> x = rand(Float32[NaN,1,2,3.0], 1_000_000);

julia> @btime findmax(skip(isnan, $x))
  5.855 ms (0 allocations: 0 bytes)

julia> @btime argmax(last,Iterators.filter(!isnan∘last,pairs($x)))
  6.854 ms (0 allocations: 0 bytes)

julia> @btime argmax(first, Iterators.filter(!isnan∘first, zip($x, keys($x))))
  7.034 ms (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [February 1, 2023, 11:22pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/39 "2023-02-01T23:22:05Z")

</div>

Sorry, I forgot to indicate the julia version:

> **versioninfo()**
>
> ```julia
> julia> versioninfo()
> Julia Version 1.9.0-beta3
> Commit 24204a7344 (2023-01-18 07:20 UTC)
> Platform Info:
> OS: Windows (x86_64-w64-mingw32)
> CPU: 8 × Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz
> WORD_SIZE: 64
> LIBM: libopenlibm
> LLVM: libLLVM-14.0.6 (ORCJIT, icelake-client)
> Threads: 8 on 8 virtual cores
> 
> ```

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [February 1, 2023, 11:26pm UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/40 "2023-02-01T23:26:57Z")

</div>

This is what I get:

```julia
julia> @btime findmax_nan($x)
  1.179 ms (0 allocations: 0 bytes)
(3.0f0, 1)

julia> @btime argmax(first, Iterators.filter(!isnan∘first, zip($x, keys($x))))
  3.121 ms (0 allocations: 0 bytes)
(3.0f0, 1)

julia> @btime findmax(skip(isnan, $x))
  3.156 ms (0 allocations: 0 bytes)
(3.0f0, 1)

```

> **Julia 1.9.0-beta3**
>
> ```julia
> julia> versioninfo()
> Julia Version 1.9.0-beta3
> Commit 24204a7344 (2023-01-18 07:20 UTC)
> Platform Info:
> OS: Windows (x86_64-w64-mingw32)
> CPU: 20 × 12th Gen Intel(R) Core(TM) i9-12900HK
> WORD_SIZE: 64
> LIBM: libopenlibm
> LLVM: libLLVM-14.0.6 (ORCJIT, alderlake)
> Threads: 20 on 20 virtual cores
> 
> ```

~~I suspect [this](https://discourse.julialang.org/t/range-statistics/92518/24).~~

Edit: This seems to be a case where the structure of the problem allows for `findmax_nan` to be a more optimal solution than using `Iterators.filter` or `skip`. For example, if `x` has less `NaN` values, then `findmax(skip(isnan, x))` runs faster, and in the case where there are no `NaN`s, it runs nearly as fast (1.45ms here).

Personally I’d rather drop `findmax(skip(isnan, x))` into my code than write my own `findmax_nan` function, unless it was a very hot loop that justified such an optimization, considering that the former pattern is less error-prone and more generally applicable. But the context of the OP (about fastmath) makes this off-topic.

---

<div class="post-metadata">

**Author:** ![dankelley](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dankelley/32/33539_2.png) [@dankelley](https://discourse.julialang.org/u/dankelley)\
**Post date:** [February 2, 2023, 11:22am UTC](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736/41 "2023-02-02T11:22:35Z")

</div>

Other language R:

```julia
min(NaN, 5.0)
min(5.0, NaN)
min(NaN, 5.0, na.rm=TRUE)
min(5.0, NaN, na.rm=TRUE)

```

The values returned are NaN, NaN, 5.0 and 5.0 for the above.

PS. I’m not saying that R is really analogous with Julia, but just answering the question as posed.

[Previous page](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736.md?page=1)

[Next page](https://discourse.julialang.org/t/min-nan-5-nan-why-no-fastmath/93736.md?page=3)
