diff --git a/NEWS.md b/NEWS.md index 18ecd24420d44..fbf3700604922 100644 --- a/NEWS.md +++ b/NEWS.md @@ -317,7 +317,7 @@ Library improvements * The `MathConst` type has been renamed `Irrational` ([#11922]). - * `isapprox` now has simpler and more sensible default tolerances ([#12393]). + * `isapprox` now has simpler and more sensible default tolerances ([#12393]), supports arrays, and has synonyms `≈` ([U+2248](http://www.fileformat.info/info/unicode/char/2248/index.htm), LaTeX `\approx`) and `≉` ([U+2249](http://www.fileformat.info/info/unicode/char/2249/index.htm), LaTeX `\napprox`) for `isapprox` and `!isapprox`, respectively ([#12472]). * Numbers @@ -1563,6 +1563,7 @@ Too numerous to mention. [#11891]: https://github.com/JuliaLang/julia/issues/11891 [#11922]: https://github.com/JuliaLang/julia/issues/11922 [#11985]: https://github.com/JuliaLang/julia/issues/11985 +[#12025]: https://github.com/JuliaLang/julia/issues/12025 [#12031]: https://github.com/JuliaLang/julia/issues/12031 [#12034]: https://github.com/JuliaLang/julia/issues/12034 [#12087]: https://github.com/JuliaLang/julia/issues/12087 @@ -1570,4 +1571,5 @@ Too numerous to mention. [#12162]: https://github.com/JuliaLang/julia/issues/12162 [#12393]: https://github.com/JuliaLang/julia/issues/12393 [#12458]: https://github.com/JuliaLang/julia/issues/12458 +[#12472]: https://github.com/JuliaLang/julia/issues/12472 [#12491]: https://github.com/JuliaLang/julia/issues/12491 diff --git a/base/docs/helpdb.jl b/base/docs/helpdb.jl index 54ffbb38c51de..ae7b8116a2a56 100644 --- a/base/docs/helpdb.jl +++ b/base/docs/helpdb.jl @@ -2935,11 +2935,18 @@ cis doc""" ```rst :: - isapprox(x::Number, y::Number; rtol::Real=sqrt(eps), atol::Real=0) + isapprox(x, y; rtol::Real=sqrt(eps), atol::Real=0) -Inexact equality comparison: ``true`` if ``abs(x-y) <= atol + rtol*max(abs(x), abs(y))``. The default ``atol`` is zero and the default ``rtol`` depends on the types of ``x`` and ``y``. +Inexact equality comparison: ``true`` if ``norm(x-y) <= atol + rtol*max(norm(x), norm(y))``. The default ``atol`` is zero and the default ``rtol`` depends on the types of ``x`` and ``y``. For real or complex floating-point values, ``rtol`` defaults to ``sqrt(eps(typeof(real(x-y))))``. This corresponds to requiring equality of about half of the significand digits. For other types, ``rtol`` defaults to zero. + +``x`` and ``y`` may also be arrays of numbers, in which case ``norm`` +defaults to ``vecnorm`` but may be changed by passing a +``norm::Function`` keyword argument. (For numbers, ``norm`` is the +same thing as ``abs``.) + +The binary operator ``≈`` is equivalent to ``isapprox`` with the default arguments, and ``x ≉ y`` is equivalent to ``!isapprox(x,y)``. ``` """ isapprox diff --git a/base/exports.jl b/base/exports.jl index 479b913d6cf68..33170d1038916 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -455,6 +455,8 @@ export zero, √, ∛, + ≈, + ≉, # specfun airy, diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 60f2f18369934..1cfa63c6e95f9 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -166,10 +166,14 @@ for f in (:round, :ceil, :floor, :trunc) end # isapprox: approximate equality of numbers -isapprox(x::Number, y::Number; rtol::Real=rtoldefault(x,y), atol::Real=0) = +function isapprox(x::Number, y::Number; rtol::Real=rtoldefault(x,y), atol::Real=0) x == y || (isfinite(x) && isfinite(y) && abs(x-y) <= atol + rtol*max(abs(x), abs(y))) +end + +const ≈ = isapprox +≉(x,y) = !(x ≈ y) # default tolerance arguments rtoldefault{T<:AbstractFloat}(::Type{T}) = sqrt(eps(T)) rtoldefault{T<:Real}(::Type{T}) = 0 -rtoldefault{T<:Number,S<:Number}(x::T, y::S) = rtoldefault(promote_type(real(T),real(S))) +rtoldefault{T<:Number,S<:Number}(x::Union(T,Type{T}), y::Union(S,Type{S})) = rtoldefault(promote_type(real(T),real(S))) diff --git a/base/linalg.jl b/base/linalg.jl index 2de5c4e455a70..67c59741176b7 100644 --- a/base/linalg.jl +++ b/base/linalg.jl @@ -5,7 +5,8 @@ module LinAlg importall Base importall ..Base.Operators import Base: USE_BLAS64, size, copy, copy_transpose!, power_by_squaring, - print_matrix, transpose!, unsafe_getindex, unsafe_setindex! + print_matrix, transpose!, unsafe_getindex, unsafe_setindex!, + isapprox export # Modules diff --git a/base/linalg/generic.jl b/base/linalg/generic.jl index 35dbf07d97a40..dc2ee43463200 100644 --- a/base/linalg/generic.jl +++ b/base/linalg/generic.jl @@ -535,3 +535,8 @@ det(x::Number) = x logdet(A::AbstractMatrix) = logdet(lufact(A)) logabsdet(A::AbstractMatrix) = logabsdet(lufact(A)) +# isapprox: approximate equality of arrays [like isapprox(Number,Number)] +function isapprox{T<:Number,S<:Number}(x::AbstractArray{T}, y::AbstractArray{S}; rtol::Real=Base.rtoldefault(T,S), atol::Real=0, norm::Function=vecnorm) + d = norm(x - y) + return isfinite(d) ? d <= atol + rtol*max(norm(x), norm(y)) : x == y +end diff --git a/test/floatapprox.jl b/test/floatapprox.jl index d8ddf69d4e118..fe76153082173 100644 --- a/test/floatapprox.jl +++ b/test/floatapprox.jl @@ -1,9 +1,9 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license # Floating point numbers - basic tests -@test isapprox(4.00000000000001, 4.0) -@test isapprox(5.0,4.999999999999993) -@test !isapprox(4.000000002, 4.00300002) +@test 4.00000000000001 ≈ 4.0 +@test 5.0 ≈ 4.999999999999993 +@test 4.000000002 ≉ 4.00300002 # Other tolerance levels @test isapprox(4.32, 4.3; rtol=0.1, atol=0.01) @@ -11,23 +11,30 @@ @test !isapprox(4.5, 4.9; rtol=0.001, atol=0.001) # Complex numbers -@test isapprox(1.0 + 1.0im, 1.0 + 1.00000000000001im) -@test isapprox(0.9999999999999 + 1.0im, 1.0 + 1.000000000000001im) +@test 1.0 + 1.0im ≈ 1.0 + 1.00000000000001im +@test 0.9999999999999 + 1.0im ≈ 1.0 + 1.000000000000001im @test isapprox(0.9999 + 1.0im, 1.0 + 1.1im; rtol = 0.0001, atol=1.1) # Complex <-> reals -@test isapprox(1.0 + 0im, 1.0000000000001) +@test 1.0 + 0im ≈ 1.0000000000001 @test isapprox(0.9999999999999, 1.0 + 0im) @test !isapprox(1.0+1im, 1.000000000000001) # Comparing NaNs -@test !isapprox(4.0,NaN) -@test !isapprox(NaN,4.0) -@test !isapprox(complex(2.3,NaN), complex(NaN,2.3)) -@test !isapprox(NaN, NaN) -@test !isapprox(complex(NaN,NaN), complex(NaN,NaN)) -@test !isapprox(complex(NaN,2.3), complex(NaN,2.3)) -@test !isapprox(complex(2.3,NaN), complex(2.3,NaN)) +@test 4.0 ≉ NaN +@test NaN ≉ 4.0 +@test complex(2.3,NaN) ≉ complex(NaN,2.3) +@test NaN ≉ NaN +@test complex(NaN,NaN) ≉ complex(NaN,NaN) +@test complex(NaN,2.3) ≉ complex(NaN,2.3) +@test complex(2.3,NaN) ≉ complex(2.3,NaN) + +# Comparing Infs +@test Inf ≈ Inf +@test Inf ≉ 1 +@test Inf ≉ -Inf +@test complex(0.0,Inf) ≈ complex(0.0,Inf) +@test complex(0.0,Inf) ≉ complex(0.0,-Inf) # Tests for integers and rationals @test isapprox(4,4) @@ -46,3 +53,9 @@ # issue #12375: @test !isapprox(1e17, 1) + +# Tests for arrays: +@test [1,2,3] ≈ [1,2,3+1e-9] +@test [0,1] ≈ [1e-9, 1] +@test [0,Inf] ≈ [0,Inf] +@test [0,Inf] ≉ [0,-Inf]