Skip to content

Commit a22506b

Browse files
authored
Improve isapprox (#216)
This changes the association between absolute tolerance and relative tolerance from additive (`+`) to `max` to match the behavior in `Base` of Julia v1. This also fixes an overflow problem and a rounding problem. This speeds up the operation with default tolerance settings.
1 parent 735b7f9 commit a22506b

File tree

3 files changed

+37
-5
lines changed

3 files changed

+37
-5
lines changed

src/FixedPointNumbers.jl

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,31 @@ end
9393
"""
9494
isapprox(x::FixedPoint, y::FixedPoint; rtol=0, atol=max(eps(x), eps(y)))
9595
96-
For FixedPoint numbers, the default criterion is that `x` and `y` differ by no more than `eps`, the separation between adjacent fixed-point numbers.
96+
For FixedPoint numbers, the default criterion is that `x` and `y` differ by no
97+
more than `eps`, the separation between adjacent fixed-point numbers.
9798
"""
98-
function isapprox(x::T, y::T; rtol=0, atol=max(eps(x), eps(y))) where {T <: FixedPoint}
99-
maxdiff = T(atol+rtol*max(abs(x), abs(y)))
100-
rx, ry, rd = reinterpret(x), reinterpret(y), reinterpret(maxdiff)
101-
abs(signed(widen1(rx))-signed(widen1(ry))) <= rd
99+
@inline function isapprox(x::X, y::X; rtol=0, atol=eps(X)) where {X <: FixedPoint}
100+
n, m = minmax(x, y) # m >= n
101+
if rtol == zero(rtol)
102+
_isapprox_atol(m, n, atol)
103+
elseif atol == zero(atol)
104+
_isapprox_rtol(m, n, rtol)
105+
else
106+
_isapprox_atol(m, n, atol) | _isapprox_rtol(m, n, rtol)
107+
end
102108
end
103109
function isapprox(x::FixedPoint, y::FixedPoint; rtol=0, atol=max(eps(x), eps(y)))
104110
isapprox(promote(x, y)...; rtol=rtol, atol=atol)
105111
end
112+
function _isapprox_atol(m::X, n::X, atol::X) where {X <: FixedPoint}
113+
unsigned(m.i - n.i) <= unsigned(max(atol, zero(X)).i)
114+
end
115+
function _isapprox_atol(m::X, n::X, atol) where {X <: FixedPoint}
116+
unsigned(m.i - n.i) <= div(atol, eps(X))
117+
end
118+
function _isapprox_rtol(m::X, n::X, rtol) where {X <: FixedPoint}
119+
unsigned(m.i - n.i) <= rtol * max(unsigned(abs(m.i)), unsigned(abs(n.i)))
120+
end
106121

107122
# predicates
108123
isinteger(x::FixedPoint) = x == trunc(x) # TODO: use floor(x) when dropping support for Fixed{Int8,8}

test/fixed.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,15 @@ end
410410
@test all(x -> x + eps(F) x, xs)
411411
@test !any(x -> x - eps(F) x + eps(F), xs)
412412
end
413+
414+
@test isapprox(-0.5Q0f7, -1Q0f7, rtol=0.5, atol=0) # issue 209
415+
@test isapprox(typemin(Q0f7), typemax(Q0f7), rtol=2.0)
416+
@test !isapprox(zero(Q0f7), typemax(Q0f7), rtol=0.9)
417+
@test isapprox(zero(Q0f7), eps(Q0f7), rtol=1e-6) # atol = eps(Q0f7)
418+
@test !isapprox(eps(Q0f7), zero(Q0f7), rtol=1e-6, atol=1e-6)
419+
@test !isapprox(1.0Q6f1, 1.5Q6f1, rtol=0.3, atol=0) # 1.5 * 0.3 < eps(Q6f1)
420+
421+
@test isapprox(eps(Q8f7), eps(Q0f7), rtol=1e-6)
413422
end
414423

415424
@testset "clamp" begin

test/normed.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,14 @@ end
431431
@test all(x -> x + eps(N) x, xs)
432432
@test !any(x -> x - eps(N) x + eps(N), xs)
433433
end
434+
435+
@test isapprox(typemin(N0f8), typemax(N0f8), rtol=1.0)
436+
@test !isapprox(zero(N0f8), typemax(N0f8), rtol=0.9)
437+
@test isapprox(zero(N0f8), eps(N0f8), rtol=1e-6) # atol = eps(N0f8)
438+
@test !isapprox(eps(N0f8), zero(N0f8), rtol=1e-6, atol=1e-6)
439+
@test !isapprox(0.66N6f2, 1.0N6f2, rtol=0.3, atol=0) # 1.0 * 0.3 < eps(N6f2)
440+
441+
@test isapprox(eps(N8f8), eps(N0f8), rtol=1e-6)
434442
end
435443

436444
@testset "comparison" begin

0 commit comments

Comments
 (0)