From 50515b44d38f27a32f9e182f7b71b1d51c656775 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 11 Aug 2015 12:18:51 -0400 Subject: [PATCH] max and min of NaN return NaN (closes #7866) (fixes #12552) --- NEWS.md | 2 ++ base/math.jl | 11 +++++------ base/mpfr.jl | 4 ++++ base/reduce.jl | 14 +++----------- test/mpfr.jl | 4 ++-- test/numbers.jl | 30 ++++++++++++++++++------------ test/reduce.jl | 12 ++++++------ 7 files changed, 40 insertions(+), 37 deletions(-) diff --git a/NEWS.md b/NEWS.md index be26b2d483af33..5be1817dd2c35c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -334,6 +334,8 @@ Library improvements * The function `primesmask` which generates a prime sieve for a user defined closed interval is now exported ([#12025]). + * `max`, `min`, and related functions (`maxmin`, `maximum`, `minimum`, `extrema`) now return `NaN` for `NaN` arguments. + * Random numbers * Streamlined random number generation APIs [#8246]. diff --git a/base/math.jl b/base/math.jl index 6682f349035ff4..f027277bf71bf9 100644 --- a/base/math.jl +++ b/base/math.jl @@ -171,18 +171,17 @@ for f in (:atan2, :hypot) end max{T<:AbstractFloat}(x::T, y::T) = ifelse((y > x) | (signbit(y) < signbit(x)), - ifelse(isnan(y), x, y), ifelse(isnan(x), y, x)) + ifelse(isnan(x), x, y), ifelse(isnan(y), y, x)) @vectorize_2arg Real max min{T<:AbstractFloat}(x::T, y::T) = ifelse((y < x) | (signbit(y) > signbit(x)), - ifelse(isnan(y), x, y), ifelse(isnan(x), y, x)) + ifelse(isnan(x), x, y), ifelse(isnan(y), y, x)) @vectorize_2arg Real min -minmax{T<:AbstractFloat}(x::T, y::T) = ifelse(isnan(x-y), ifelse(isnan(x), (y, y), (x, x)), - ifelse((y < x) | (signbit(y) > signbit(x)), (y, x), - ifelse((y > x) | (signbit(y) < signbit(x)), (x, y), - ifelse(x == x, (x, x), (y, y))))) +minmax{T<:AbstractFloat}(x::T, y::T) = + ifelse(isnan(x) | isnan(y), ifelse(isnan(x), (x,x), (y,y)), + ifelse((y > x) | (signbit(x) > signbit(y)), (x,y), (y,x))) ldexp(x::Float64,e::Integer) = ccall((:scalbn,libm), Float64, (Float64,Int32), x, Int32(e)) ldexp(x::Float32,e::Integer) = ccall((:scalbnf,libm), Float32, (Float32,Int32), x, Int32(e)) diff --git a/base/mpfr.jl b/base/mpfr.jl index 5ac9dcf9653002..e5ed8c13ab2d9f 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -563,12 +563,16 @@ function log1p(x::BigFloat) end function max(x::BigFloat, y::BigFloat) + isnan(x) && return x + isnan(y) && return y z = BigFloat() ccall((:mpfr_max, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[end]) return z end function min(x::BigFloat, y::BigFloat) + isnan(x) && return x + isnan(y) && return y z = BigFloat() ccall((:mpfr_min, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[end]) return z diff --git a/base/reduce.jl b/base/reduce.jl index ce34985e74d46d..42775d97938e0d 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -332,19 +332,11 @@ function extrema(itr) s = start(itr) done(itr, s) && throw(ArgumentError("collection must be non-empty")) (v, s) = next(itr, s) - while v != v && !done(itr, s) - (x, s) = next(itr, s) - v = x - end - vmin = v - vmax = v + vmin = vmax = v while !done(itr, s) (x, s) = next(itr, s) - if x > vmax - vmax = x - elseif x < vmin - vmin = x - end + vmax = max(x, vmax) + vmin = min(x, vmin) end return (vmin, vmax) end diff --git a/test/mpfr.jl b/test/mpfr.jl index f4849a3878d06c..cf14bf8a1caa2e 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -292,8 +292,8 @@ y = BigFloat(2) @test max(x,y) == x @test min(x,y) == y y = BigFloat(NaN) -@test max(x,y) == x -@test min(x,y) == x +@test isnan(max(x,y)) +@test isnan(min(x,y)) @test isnan(max(y,y)) @test isnan(min(y,y)) diff --git a/test/numbers.jl b/test/numbers.jl index 15c5ea77dbb332..31668aad4428b1 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -54,32 +54,38 @@ @test 2.0 * 3.0 == 6. @test min(1.0,1) == 1 +const ≅ = isequal # convenient for comparing NaNs + # min, max and minmax @test minmax(5, 3) == (3, 5) @test minmax(3., 5.) == (3., 5.) @test minmax(5., 3.) == (3., 5.) -@test minmax(3., NaN) == (3., 3.) -@test minmax(NaN, 3.) == (3., 3.) -@test isequal(minmax(NaN, NaN), (NaN, NaN)) +@test minmax(3., NaN) ≅ (NaN, NaN) +@test minmax(NaN, 3) ≅ (NaN, NaN) +@test minmax(Inf, NaN) ≅ (NaN, NaN) +@test minmax(NaN, Inf) ≅ (NaN, NaN) +@test minmax(-Inf, NaN) ≅ (NaN, NaN) +@test minmax(NaN, -Inf) ≅ (NaN, NaN) +@test minmax(NaN, NaN) ≅ (NaN, NaN) @test min(-0.0,0.0) === min(0.0,-0.0) @test max(-0.0,0.0) === max(0.0,-0.0) @test minmax(-0.0,0.0) === minmax(0.0,-0.0) @test max(-3.2, 5.1) == max(5.1, -3.2) == 5.1 @test min(-3.2, 5.1) == min(5.1, -3.2) == -3.2 @test max(-3.2, Inf) == max(Inf, -3.2) == Inf -@test max(-3.2, NaN) == max(NaN, -3.2) == -3.2 +@test max(-3.2, NaN) ≅ max(NaN, -3.2) ≅ NaN @test min(5.1, Inf) == min(Inf, 5.1) == 5.1 @test min(5.1, -Inf) == min(-Inf, 5.1) == -Inf -@test min(5.1, NaN) == min(NaN, 5.1) == 5.1 -@test min(5.1, -NaN) == min(-NaN, 5.1) == 5.1 +@test min(5.1, NaN) ≅ min(NaN, 5.1) ≅ NaN +@test min(5.1, -NaN) ≅ min(-NaN, 5.1) ≅ NaN @test minmax(-3.2, 5.1) == (min(-3.2, 5.1), max(-3.2, 5.1)) @test minmax(-3.2, Inf) == (min(-3.2, Inf), max(-3.2, Inf)) -@test minmax(-3.2, NaN) == (min(-3.2, NaN), max(-3.2, NaN)) -@test (max(Inf,NaN), max(-Inf,NaN), max(Inf,-NaN), max(-Inf,-NaN)) == (Inf, -Inf, Inf, -Inf) -@test (max(NaN,Inf), max(NaN,-Inf), max(-NaN,Inf), max(-NaN,-Inf)) == (Inf, -Inf, Inf, -Inf) -@test (min(Inf,NaN), min(-Inf,NaN), min(Inf,-NaN), min(-Inf,-NaN)) == (Inf, -Inf, Inf, -Inf) -@test (min(NaN,Inf), min(NaN,-Inf), min(-NaN,Inf), min(-NaN,-Inf)) == (Inf, -Inf, Inf, -Inf) -@test minmax(-Inf,NaN) == (min(-Inf,NaN), max(-Inf,NaN)) +@test minmax(-3.2, NaN) ≅ (min(-3.2, NaN), max(-3.2, NaN)) +@test (max(Inf,NaN), max(-Inf,NaN), max(Inf,-NaN), max(-Inf,-NaN)) ≅ (NaN,NaN,NaN,NaN) +@test (max(NaN,Inf), max(NaN,-Inf), max(-NaN,Inf), max(-NaN,-Inf)) ≅ (NaN,NaN,NaN,NaN) +@test (min(Inf,NaN), min(-Inf,NaN), min(Inf,-NaN), min(-Inf,-NaN)) ≅ (NaN,NaN,NaN,NaN) +@test (min(NaN,Inf), min(NaN,-Inf), min(-NaN,Inf), min(-NaN,-Inf)) ≅ (NaN,NaN,NaN,NaN) +@test minmax(-Inf,NaN) ≅ (min(-Inf,NaN), max(-Inf,NaN)) # fma let x = Int64(7)^7 diff --git a/test/reduce.jl b/test/reduce.jl index c5accb3db1542f..87fa006410b555 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -144,13 +144,13 @@ prod2(itr) = invoke(prod, Tuple{Any}, itr) @test isnan(minimum([NaN])) @test isequal(extrema([NaN]), (NaN, NaN)) -@test maximum([NaN, 2., 3.]) == 3. -@test minimum([NaN, 2., 3.]) == 2. -@test extrema([NaN, 2., 3.]) == (2., 3.) +@test isnan(maximum([NaN, 2., 3.])) +@test isnan(minimum([NaN, 2., 3.])) +@test isequal(extrema([NaN, 2., 3.]), (NaN,NaN)) -@test maximum([4., 3., NaN, 5., 2.]) == 5. -@test minimum([4., 3., NaN, 5., 2.]) == 2. -@test extrema([4., 3., NaN, 5., 2.]) == (2., 5.) +@test isnan(maximum([4., 3., NaN, 5., 2.])) +@test isnan(minimum([4., 3., NaN, 5., 2.])) +@test isequal(extrema([4., 3., NaN, 5., 2.]), (NaN,NaN)) @test maxabs(Int[]) == 0 @test_throws ArgumentError Base.minabs(Int[])