Operators · Lesson 2.2

Comparison

Source
Compare two values with == != < <= > >= and get a bool back.

Programs constantly ask questions about their values. Is the user old enough? Is the tank empty? Did the score beat the record? A comparison asks one such question about two values, and the answer is always a bool — true or false. That answer is what the next part, Control flow, uses to decide what runs.

Six questions

OperatorAsks17 and 5 give
==equal?false
!=not equal?true
<less than?false
<=less than or equal?false
>greater than?true
>=greater than or equal?true

Equality is written with two equals signs. A single = is assignment, which changes a variable rather than asking about it.

let a: int32 = 17;
let b: int32 = 5;
PrintLine("{} == {}  is {}", a, b, a == b);
PrintLine("{} != {}  is {}", a, b, a != b);

A comparison is a value

The answer is an ordinary bool, so it can be printed, passed along, or kept in a variable with a name that says what it means:

let age: int32 = 18;
let adult = age >= 18;
PrintLine("age {} is adult: {}", age, adult);

adult is a bool, inferred from the comparison. Naming a condition this way often makes the code that later tests it read like a sentence.

Both sides must agree

Like arithmetic, a comparison needs operands of compatible types. Two integers of the same signedness may differ in width — an int8 compares with an int64, because the narrower one widens without loss. Everything else is refused:

LeftRightResult
int32int32compared
int8int64compared — the int8 widens
int32uint32refused — the operands differ in signedness
int32float64refused — convert one side with as first

Characters compare by code point

A character is a number underneath, as Convert showed, and comparing characters compares those numbers. Every capital letter comes before every lowercase one, so 'Z' sorts ahead of 'a':

let upper: char32 = 'Z';
let lower: char32 = 'a';
PrintLine("'{}' < '{}'  is {}", upper, lower, upper < lower);

That is alphabetical order only within one case — worth remembering before sorting names.

Floats and exact equality

Float showed that 0.1 + 0.2 is not exactly 0.3. == sees the difference, however tiny:

let sum: float64 = 0.1 + 0.2;
PrintLine("0.1 + 0.2 == 0.3  is {}", sum == 0.3);
PrintLine("0.1 + 0.2 >  0.3  is {}", sum > 0.3);

The sum lands just above 0.3, so == says false and > says true. Ordering comparisons on floats are fine; be wary of exact == between floats that came out of arithmetic. Ask instead whether two values are close enough — whether their difference is below some small tolerance you choose.

Comparisons do not chain

In maths, 1 < age < 30 means "age is between 1 and 30". In Rux, it does not:

flowchart LR
    expr["1 < age < 30"] --> first["1 < age<br/>is worked out first"]
    first --> b["a bool:<br/>true or false"]
    b --> second["that bool < 30"]
    second --> err["error: operator '<' cannot compare<br/>left operand 'bool8'<br/>with right operand 'int'"]

The first comparison produces a bool, and a bool cannot be compared with a number. Asking two questions at once takes && — 1 < age && age < 30 — the subject of the next lesson.

The program

The whole lesson is one package in the Examples repository. Its comments explain every step.

Src/Main.rux
// The six comparison operators ask a yes-or-no question about two values:
// `==` equal, `!=` not equal, `<` less, `<=` less or equal, `>` greater and
// `>=` greater or equal. Whatever the operands are, the answer is a `bool`, so
// it can be printed or kept in a variable like any other value.
import Io::PrintLine;

func Main() -> int {
    let a: int32 = 17;
    let b: int32 = 5;
    PrintLine("{} == {}  is {}", a, b, a == b);
    PrintLine("{} != {}  is {}", a, b, a != b);
    PrintLine("{} <  {}  is {}", a, b, a < b);
    PrintLine("{} <= {}  is {}", a, b, a <= b);
    PrintLine("{} >  {}  is {}", a, b, a > b);
    PrintLine("{} >= {}  is {}", a, b, a >= b);

    // A comparison is an ordinary expression, and its result is a value.
    let age: int32 = 18;
    let adult = age >= 18;
    PrintLine("age {} is adult: {}", age, adult);

    // Characters compare by code point. Every capital letter comes before every
    // lowercase one, so 'Z' sorts ahead of 'a'.
    let upper: char32 = 'Z';
    let lower: char32 = 'a';
    PrintLine("'{}' < '{}'  is {}", upper, lower, upper < lower);

    // Floats are stored approximately, so a sum that looks exact on paper can
    // miss by a hair, and `==` sees the hair: this sum lands just above 0.3. Be
    // wary of exact `==` between floats that came out of arithmetic.
    let sum: float64 = 0.1 + 0.2;
    PrintLine("0.1 + 0.2 == 0.3  is {}", sum == 0.3);
    PrintLine("0.1 + 0.2 >  0.3  is {}", sum > 0.3);

    // Comparisons do not chain the way they do in maths. `1 < age < 30` would
    // compare `1 < age`, a `bool`, against 30, and the compiler rejects that.
    // Asking two questions at once needs `&&`, the subject of the next lesson.
    return 0;
}

Run it

cd Examples/Operators/Comparison
rux run
17 == 5  is false
17 != 5  is true
17 <  5  is false
17 <= 5  is false
17 >  5  is true
17 >= 5  is true
age 18 is adult: true
'Z' < 'a'  is true
0.1 + 0.2 == 0.3  is false
0.1 + 0.2 >  0.3  is true

Common mistakes

Chaining comparisons.
1 < age < 30 fails with error: operator '<' cannot compare left operand 'bool8' with right operand 'int': the left half is already a bool. Write 1 < age && age < 30.
Comparing an integer with a float.
When age is an int32 and limit is a float64, age > limit fails with error: operator '>' cannot compare left operand 'int32' with right operand 'float64'. Convert one side with as.
Comparing signed with unsigned.
An int32 and a uint32 are refused too — note: the operands differ in signedness, so neither converts to the other's type — and the compiler's help suggests converting one operand with as.
Testing floats for exact equality.
0.1 + 0.2 == 0.3 is false. Compare the difference against a small tolerance instead.

Try it yourself

  1. Compare 'a' with 'b', and '9' with 'A'. Print the code points with as uint32 to see why.
  2. Declare let temperature: int32 = 21; and a bool named comfortable that is true from 18 to 24. You will need && from the next lesson.
  3. Print 0.1 + 0.2 - 0.3 to see how far the sum misses.
  4. Write 1 < age < 30 and read the error, then fix it.

Learn more