From afe7b8e82ee5f57efffae650d1778291567ea9ee 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 | 4 ++-- 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(+), 39 deletions(-) diff --git a/NEWS.md b/NEWS.md index b5167691db82a..50d3798a7e2f4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,8 +31,7 @@ This section lists changes that do not have deprecation warnings. Library improvements -------------------- -Compiler/Runtime improvements ------------------------------ + * `max`, `min`, and related functions (`minmax`, `maximum`, `minimum`, `extrema`) now return `NaN` for `NaN` arguments ([#12563]). Deprecated or removed --------------------- @@ -551,6 +550,7 @@ Language tooling improvements [#11242]: https://github.com/JuliaLang/julia/issues/11242 [#11688]: https://github.com/JuliaLang/julia/issues/11688 [#12231]: https://github.com/JuliaLang/julia/issues/12231 +[#12563]: https://github.com/JuliaLang/julia/issues/12563 [#12819]: https://github.com/JuliaLang/julia/issues/12819 [#12872]: https://github.com/JuliaLang/julia/issues/12872 [#13062]: https://github.com/JuliaLang/julia/issues/13062 diff --git a/base/math.jl b/base/math.jl index 46b47cc4876bf..c0c4736f3058f 100644 --- a/base/math.jl +++ b/base/math.jl @@ -314,16 +314,15 @@ atan2(y::Float64, x::Float64) = ccall((:atan2,libm), Float64, (Float64, Float64, atan2(y::Float32, x::Float32) = ccall((:atan2f,libm), Float32, (Float32, Float32), y, x) 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)) 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)) -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))) """ diff --git a/base/mpfr.jl b/base/mpfr.jl index c8d28d1b32468..25c92fa3c6ee8 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -584,12 +584,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[]) 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[]) return z diff --git a/base/reduce.jl b/base/reduce.jl index e685d73050def..05d6c9a064bea 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -503,19 +503,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 0c62de5765317..cbef129d71a6e 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -301,8 +301,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 3174bd531ba8f..4b1939ddfb66d 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -54,6 +54,8 @@ @test 2.0 * 3.0 == 6. @test min(1.0,1) == 1 +const ≅ = isequal # convenient for comparing NaNs + # min, max and minmax @test min(1) === 1 @test max(1) === 1 @@ -61,28 +63,32 @@ @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 9f0e91dfaf2d3..91aabb093f434 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[])