How can I redefine the operator "≈" to my desired tolerance

Currently, “The binary operator ≈ is equivalent to isapprox with the default arguments”
Hence: x ≈ y is equivalent to isapprox(x,y)

How can I redefine the operator w/ my own custom tolerance:
such that: x ≈ y is equivalent to isapprox(x,y,atol=.01)

ε = .1; 
≊(a,b;ε=ε) = isapprox(a,b;atol=ε)

julia> 4.9 ≊ 5.0
true
5 Likes

is just a function in Base, so you can do

julia> import Base.≈

julia> ≈(a::Float64, b::Float64) = isapprox(a, b, atol = 0.1)
isapprox (generic function with 11 methods)

julia> 4.9 ≈ 5.0
true

but I would be careful about redefining how basic functionality like this works, at a minimum it makes it harder for others to understand your code. Choosing your own alternative Unicode identifier as you do above is preferable in my opinion, in these situations where I modify a defined infix operator I often add suffixes to indicate what the modification is doing, e.g. in this case maybe ≈₀₁

7 Likes

This looks like a decent application for a macro:

function _approx!(ex::Expr, kwargs)
    _approx!.(ex.args, Ref(kwargs))
    if ex.head == :call && ex.args[1] == :≈
        append!(ex.args, kwargs)
    end
end

_approx!(x, kwargs) = nothing

macro approx(args...)
    kwargs = [Expr(:kw, arg.args...) for arg in args[1:end-1]]
    ex = args[end]
    _approx!(ex, kwargs)
    return ex
end

With this you can do:

julia> @approx atol=0.1 rtol=0.01 begin
           @show 1 ≈ 1.05
           @show 1 ≈ 2
       end
≈(1, 1.05, atol = 0.1, rtol = 0.01) = true
≈(1, 2, atol = 0.1, rtol = 0.01) = false
7 Likes

This is nice because it is consistent with the behavior in Test.jl:

julia> using Test

julia> @test 0.5 ≈ 0.6 atol=0.1
Test Passed
5 Likes

Note that there are tons of unassigned operators, so you can do something like

≅(a, b) = isapprox(a, b; atol = 0.1)

and not have to worry about type piracy.

You can always just redefine within your module:

≈(x,y) = isapprox(x,y,atol=0.01)

This doesn’t affect Base.≈ and is not type-piracy… it is defining a new module-local function of the same name. Of course, this could be confusing for someone reading your code, so it might be clearer to give it a different name like ≈₂ (which is a valid operator name in Julia).

But if you just want to change the tolerance in some @test calls, you can also use @test x ≈ y atol=0.01 to pass keyword arguments to .

6 Likes

Another option if you really want to use , is to define it in a local scope just around where you are using it.

julia> let ≈(a,b;ε=0.1) = isapprox(a,b;atol=ε)
           4.9 ≈ 5.0
       end
true

This may be clearer than defining it at the module level.

7 Likes