Skip to content

Commit

Permalink
fix JuliaLang#12375 (fix scaling of isapprox via more sensible defaul…
Browse files Browse the repository at this point in the history
…t tolerances)
  • Loading branch information
stevengj committed Jul 30, 2015
1 parent 85bbc1f commit 52e7171
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 40 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ Library improvements

* The `MathConst` type has been renamed `Irrational` ([#11922]).

* `isapprox` now has simpler and more sensible default tolerances ([#12393]).

* Random numbers

* Streamlined random number generation APIs [#8246].
Expand Down Expand Up @@ -1542,3 +1544,4 @@ Too numerous to mention.
[#12034]: https://github.com/JuliaLang/julia/issues/12034
[#12087]: https://github.com/JuliaLang/julia/issues/12087
[#12137]: https://github.com/JuliaLang/julia/issues/12137
[#12393]: https://github.com/JuliaLang/julia/issues/12393
33 changes: 6 additions & 27 deletions base/floatfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -165,33 +165,12 @@ for f in (:round, :ceil, :floor, :trunc)
end
end

# isapprox: Tolerant comparison of floating point numbers
function isapprox(x::FloatingPoint, y::FloatingPoint; rtol::Real=rtoldefault(x,y), atol::Real=atoldefault(x,y))
(isinf(x) || isinf(y)) ? x == y : abs(x-y) <= atol + rtol.*max(abs(x), abs(y))
end

# promotion of non-floats
isapprox(x::Real, y::FloatingPoint; rtol::Real=rtoldefault(x, y), atol::Real=atoldefault(x, y)) = isapprox(promote(x, y)...; rtol=rtol, atol=atol)
isapprox(x::FloatingPoint, y::Real; rtol::Real=rtoldefault(x, y), atol::Real=atoldefault(x, y)) = isapprox(promote(x, y)...; rtol=rtol, atol=atol)

# other real numbers
isapprox(x::Real, y::Real; rtol::Real=0, atol::Real=0) = abs(x-y) <= atol

# complex numbers
isapprox(z::Complex, w::Complex; rtol::Real=rtoldefault(abs(z), abs(w)), atol::Real=atoldefault(abs(z), abs(w))) = abs(z-w) <= atol + rtol*max(abs(z), abs(w))

# real-complex combinations
isapprox(x::Real, z::Complex; rtol::Real=rtoldefault(x, abs(z)), atol::Real=atoldefault(x, abs(z))) = isapprox(complex(x), z; rtol=rtol, atol=atol)
isapprox(z::Complex, x::Real; rtol::Real=rtoldefault(x, abs(z)), atol::Real=atoldefault(x, abs(z))) = isapprox(complex(x), z; rtol=rtol, atol=atol)
# isapprox: approximate equality of numbers
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)))

# default tolerance arguments
rtoldefault(x::FloatingPoint, y::FloatingPoint) = cbrt(max(eps(x), eps(y)))
atoldefault(x::FloatingPoint, y::FloatingPoint) = sqrt(max(eps(x), eps(y)))
rtoldefault{T<:FloatingPoint}(::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)))

# promotion of non-floats
for fun in (:rtoldefault, :atoldefault)
@eval begin
($fun)(x::Real, y::FloatingPoint) = ($fun)(promote(x,y)...)
($fun)(x::FloatingPoint, y::Real) = ($fun)(promote(x,y)...)
end
end
12 changes: 3 additions & 9 deletions doc/stdlib/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -417,17 +417,11 @@ Mathematical Operators
Mathematical Functions
----------------------

.. function:: isapprox(x::Number, y::Number; rtol::Real=cbrt(maxeps), atol::Real=sqrt(maxeps))
.. function:: isapprox(x::Number, y::Number; rtol::Real=sqrt(eps), atol::Real=0)

Inexact equality comparison - behaves slightly different depending on types of input args:
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``.

* For ``FloatingPoint`` numbers, ``isapprox`` returns ``true`` if ``abs(x-y) <= atol + rtol*max(abs(x), abs(y))``.

* For ``Integer`` and ``Rational`` numbers, ``isapprox`` returns ``true`` if ``abs(x-y) <= atol``. The `rtol` argument is ignored. If one of ``x`` and ``y`` is ``FloatingPoint``, the other is promoted, and the method above is called instead.

* For ``Complex`` numbers, the distance in the complex plane is compared, using the same criterion as above.

For default tolerance arguments, ``maxeps = max(eps(abs(x)), eps(abs(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.

.. function:: sin(x)

Expand Down
12 changes: 8 additions & 4 deletions test/fastmath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -138,18 +138,22 @@ for T in (Complex64, Complex128, Complex{BigFloat})
half = (1+1im)/T(2)
third = (1-1im)/T(3)

# some of these functions promote their result to double
# precision, but we want to check equality at precision T
rtol = Base.rtoldefault(real(T))

for f in (:+, :-, :abs, :abs2, :conj, :inv, :sign,
:acos, :acosh, :asin, :asinh, :atan, :atanh, :cos,
:cosh, :exp10, :exp2, :exp, :expm1, :log10, :log1p,
:log2, :log, :sin, :sinh, :sqrt, :tan, :tanh)
@test isapprox((@eval @fastmath $f($half)), (@eval $f($half)))
@test isapprox((@eval @fastmath $f($third)), (@eval $f($third)))
@test isapprox((@eval @fastmath $f($half)), (@eval $f($half)), rtol=rtol)
@test isapprox((@eval @fastmath $f($third)), (@eval $f($third)), rtol=rtol)
end
for f in (:+, :-, :*, :/, :(==), :!=, :^)
@test isapprox((@eval @fastmath $f($half, $third)),
(@eval $f($half, $third)))
(@eval $f($half, $third)), rtol=rtol)
@test isapprox((@eval @fastmath $f($third, $half)),
(@eval $f($third, $half)))
(@eval $f($third, $half)), rtol=rtol)
end
end

Expand Down
3 changes: 3 additions & 0 deletions test/floatapprox.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@
# Notably missing from this test suite at the moment
# * Tests for other magnitudes of numbers - very small, very big, and combinations of small and big
# * Tests for various odd combinations of types, e.g. isapprox(x::Integer, y::Rational)

# issue #12375:
@test !isapprox(1e17, 1)

0 comments on commit 52e7171

Please sign in to comment.