# Comparisons

## Syntax

• x < y # if `x` is strictly less than `y`
• x > y # if `x` is strictly greater than `y`
• x == y # if `x` is equal to `y`
• x === y # alternatively `x ≡ y`, if `x` is egal to `y`
• x ≤ y # alternatively `x <= y`, if `x` is less than or equal to `y`
• x ≥ y # alternatively `x >= y`, if `x` is greater than or equal to `y`
• x ≠ y # alternatively `x != y`, if `x` is not equal to `y`
• x ≈ y # if `x` is approximately equal to `y`

## Remarks

Be careful about flipping comparison signs around. Julia defines many comparison functions by default without defining the corresponding flipped version. For instance, one can run

``````julia> Set(1:3) ⊆ Set(0:5)
true
``````

but it does not work to do

``````julia> Set(0:5) ⊇ Set(1:3)
ERROR: UndefVarError: ⊇ not defined
``````

## Chained Comparisons

Multiple comparison operators used together are chained, as if connected via the `&&` operator. This can be useful for readable and mathematically concise comparison chains, such as

``````# same as 0 < i && i <= length(A)
isinbounds(A, i)       = 0 < i ≤ length(A)

# same as Set() != x && issubset(x, y)
isnonemptysubset(x, y) = Set() ≠ x ⊆ y
``````

However, there is an important difference between `a > b > c` and `a > b && b > c`; in the latter, the term `b` is evaluated twice. This does not matter much for plain old symbols, but could matter if the terms themselves have side effects. For instance,

``````julia> f(x) = (println(x); 2)
f (generic function with 1 method)

julia> 3 > f("test") > 1
test
true

julia> 3 > f("test") && f("test") > 1
test
test
true
``````

Let’s take a deeper look at chained comparisons, and how they work, by seeing how they are parsed and lowered into expressions. First, consider the simple comparison, which we can see is just a plain old function call:

``````julia> dump(:(a > b))
Expr
args: Array{Any}((3,))
1: Symbol >
2: Symbol a
3: Symbol b
typ: Any
``````

Now if we chain the comparison, we notice that the parsing has changed:

``````julia> dump(:(a > b >= c))
Expr
args: Array{Any}((5,))
1: Symbol a
2: Symbol >
3: Symbol b
4: Symbol >=
5: Symbol c
typ: Any
``````

After parsing, the expression is then lowered to its final form:

``````julia> expand(:(a > b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
``````

and we note indeed that this is the same as for `a > b && b >= c`:

``````julia> expand(:(a > b && b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
``````

## Ordinal Numbers

We will look at how to implement custom comparisons by implementing a custom type, ordinal numbers. To simplify the implementation, we will focus on a small subset of these numbers: all ordinal numbers up to but not including ε₀. Our implementation is focused on simplicity, not speed; however, the implementation is not slow either.

We store ordinal numbers by their Cantor normal form. Because ordinal arithmetic is not commutative, we will take the common convention of storing most significant terms first.

``````immutable OrdinalNumber <: Number
βs::Vector{OrdinalNumber}
cs::Vector{Int}
end
``````

Since the Cantor normal form is unique, we may test equality simply through recursive equality:

0.5.0

In version v0.5, there is a very nice syntax for doing this compactly:

``````import Base: ==
α::OrdinalNumber == β::OrdinalNumber = α.βs == β.βs && α.cs == β.cs
``````
0.5.0

Otherwise, define the function as is more typical:

``````import Base: ==
==(α::OrdinalNumber, β::OrdinalNumber) = α.βs == β.βs && α.cs == β.cs
``````

To finish our order, because this type has a total order, we should overload the `isless` function:

``````import Base: isless
function isless(α::OrdinalNumber, β::OrdinalNumber)
for i in 1:min(length(α.cs), length(β.cs))
if α.βs[i] < β.βs[i]
return true
elseif α.βs[i] == β.βs[i] && α.cs[i] < β.cs[i]
return true
end
end
return length(α.cs) < length(β.cs)
end
``````

To test our order, we can create some methods to make ordinal numbers. Zero, of course, is obtained by having no terms in the Cantor normal form:

``````const ORDINAL_ZERO = OrdinalNumber([], [])
Base.zero(::Type{OrdinalNumber}) = ORDINAL_ZERO
``````

We can defined an `expω` to compute `ω^α`, and use that to compute 1 and ω:

``````expω(α) = OrdinalNumber([α], )
const ORDINAL_ONE = expω(ORDINAL_ZERO)
Base.one(::Type{OrdinalNumber}) = ORDINAL_ONE
const ω = expω(ORDINAL_ONE)
``````

We now have a fully functional ordering function on ordinal numbers:

``````julia> ORDINAL_ZERO < ORDINAL_ONE < ω < expω(ω)
true

julia> ORDINAL_ONE > ORDINAL_ZERO
true

julia> sort([ORDINAL_ONE, ω, expω(ω), ORDINAL_ZERO])

4-element Array{OrdinalNumber,1}:
OrdinalNumber(OrdinalNumber[],Int64[])
OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[],Int64[])],)
OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[],Int64[])],)],)
OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[],Int64[])],)],)],)
``````

In the last example, we see that the printing of ordinal numbers could be better, but the result is as expected.

## Standard Operators

Julia supports a very large set of comparison operators. These include

1. All of the following unicode sequences: `> < >= ≥ <= ≤ == === ≡ != ≠ !== ≢ ∈ ∉ ∋ ∌ ⊆ ⊈ ⊂ ⊄ ⊊ ∝ ∊ ∍ ∥ ∦ ∷ ∺ ∻ ∽ ∾ ≁ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≣ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿ ⊀ ⊁ ⊃ ⊅ ⊇ ⊉ ⊋ ⊏ ⊐ ⊑ ⊒ ⊜ ⊩ ⊬ ⊮ ⊰ ⊱ ⊲ ⊳ ⊴ ⊵ ⊶ ⊷ ⋍ ⋐ ⋑ ⋕ ⋖ ⋗ ⋘ ⋙ ⋚ ⋛ ⋜ ⋝ ⋞ ⋟ ⋠ ⋡ ⋢ ⋣ ⋤ ⋥ ⋦ ⋧ ⋨ ⋩ ⋪ ⋫ ⋬ ⋭ ⋲ ⋳ ⋴ ⋵ ⋶ ⋷ ⋸ ⋹ ⋺ ⋻ ⋼ ⋽ ⋾ ⋿ ⟈ ⟉ ⟒ ⦷ ⧀ ⧁ ⧡ ⧣ ⧤ ⧥ ⩦ ⩧ ⩪ ⩫ ⩬ ⩭ ⩮ ⩯ ⩰ ⩱ ⩲ ⩳ ⩴ ⩵ ⩶ ⩷ ⩸ ⩹ ⩺ ⩻ ⩼ ⩽ ⩾ ⩿ ⪀ ⪁ ⪂ ⪃ ⪄ ⪅ ⪆ ⪇ ⪈ ⪉ ⪊ ⪋ ⪌ ⪍ ⪎ ⪏ ⪐ ⪑ ⪒ ⪓ ⪔ ⪕ ⪖ ⪗ ⪘ ⪙ ⪚ ⪛ ⪜ ⪝ ⪞ ⪟ ⪠ ⪡ ⪢ ⪣ ⪤ ⪥ ⪦ ⪧ ⪨ ⪩ ⪪ ⪫ ⪬ ⪭ ⪮ ⪯ ⪰ ⪱ ⪲ ⪳ ⪴ ⪵ ⪶ ⪷ ⪸ ⪹ ⪺ ⪻ ⪼ ⪽ ⪾ ⪿ ⫀ ⫁ ⫂ ⫃ ⫄ ⫅ ⫆ ⫇ ⫈ ⫉ ⫊ ⫋ ⫌ ⫍ ⫎ ⫏ ⫐ ⫑ ⫒ ⫓ ⫔ ⫕ ⫖ ⫗ ⫘ ⫙ ⫷ ⫸ ⫹ ⫺ ⊢ ⊣`;
2. All symbols in point 1, preceded by a dot (`.`) to be made elementwise;
3. The operators `<:`, `>:`, `.!`, and `in`, which cannot be preceded by a dot (`.`).

Not all of these have a definition in the standard `Base` library. However, they are available for other packages to define and use as appropriate.

In everyday use, most of these comparison operators are not relevant. The most common ones used are the standard mathematical functions for ordering; see the Syntax section for a list.

Like most other operators in Julia, comparison operators are functions and can be called as functions. For instance, `(<)(1, 2)` is identical in meaning to `1 < 2`.

## Using ==, ===, and isequal

There are three equality operators: `==`, `===`, and `isequal`. (The last is not really an operator, but it is a function and all operators are functions.)

### When to use `==`

`==` is value equality. It returns `true` when two objects represent, in their present state, the same value.

For instance, it is obvious that

``````julia> 1 == 1
true
``````

but furthermore

``````julia> 1 == 1.0
true

julia> 1 == 1.0 + 0.0im
true

julia> 1 == 1//1
true
``````

The right hand sides of each equality above are of a different type, but they still represent the same value.

For mutable objects, like arrays, `==` compares their present value.

``````julia> A = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3

julia> B = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3

julia> C = [1, 3, 2]
3-element Array{Int64,1}:
1
3
2

julia> A == B
true

julia> A == C
false

julia> A, A = A, A  # swap 2nd and 3rd elements of A
(3,2)

julia> A
3-element Array{Int64,1}:
1
3
2

julia> A == B
false

julia> A == C
true
``````

Most of the time, `==` is the right choice.

### When to use `===`

`===` is a far stricter operation than `==`. Instead of value equality, it measures egality. Two objects are egal if they cannot be distinguished from each other by the program itself. Thus we have

``````julia> 1 === 1
true
``````

as there is no way to tell a `1` apart from another `1`. But

``````julia> 1 === 1.0
false
``````

because although `1` and `1.0` are the same value, they are of different types, and so the program can tell them apart.

Furthermore,

``````julia> A = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3

julia> B = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3

julia> A === B
false

julia> A === A
true
``````

which may at first seem surprising! How could the program distinguish between the two vectors `A` and `B`? Because vectors are mutable, it could modify `A`, and then it would behave differently from `B`. But no matter how it modifies `A`, `A` will always behave the same as `A` itself. So `A` is egal to `A`, but not egal to `B`.

Continuing along this vein, observe

``````julia> C = A
3-element Array{Int64,1}:
1
2
3

julia> A === C
true
``````

By assigning `A` to `C`, we say that `C` has aliased `A`. That is, it has become just another name for `A`. Any modifications done to `A` will be observed by `C` also. Therefore, there is no way to tell the difference between `A` and `C`, so they are egal.

### When to use `isequal`

The difference between `==` and `isequal` is very subtle. The biggest difference is in how floating point numbers are handled:

``````julia> NaN == NaN
false
``````

This possibly surprising result is defined by the IEEE standard for floating point types (IEEE-754). But this is not useful in some cases, such as sorting. `isequal` is provided for those cases:

``````julia> isequal(NaN, NaN)
true
``````

On the flip side of the spectrum, `==` treats IEEE negative zero and positive zero as the same value (also as specified by IEEE-754). These values have distinct representations in memory, however.

``````julia> 0.0
0.0

julia> -0.0
-0.0

julia> 0.0 == -0.0
true
``````

Again for sorting purposes, `isequal` distinguishes between them.

``````julia> isequal(0.0, -0.0)
false
``````

2016-08-20
2016-10-25
Julia Language Pedia
Icon