Skip to content
VLSI Mentor

Perl Part I · Module 3 · Operators and Expressions

The Three-Way Comparators: <=> and cmp

Every comparison so far has answered yes or no. These two answer a different question: does the left value come before the right one, after it, or in the same place? That three-way answer is exactly what sorting needs, which is where you will use them. They split into numeric and textual forms like every other comparison in this module, and on the same two operands they can return opposite signs. One of them also has a property no other operator in the module has — there is an input for which it returns neither a comparison nor an error, but nothing at all, and a sort built on it then produces a confidently ordered list from an answer that was never given.

Foundation13 min readPerlPart IFundamentalsComparisonSortingOperators

Part I · Module 3 · Chapter 3.7 · The Three-Way Comparators: <=> and cmp

1. A Three-Way Answer

Compare asOperatorReturns
numbers<=>-1, 0 or 1
textcmp-1, 0 or 1

<=> is called the spaceship operator, from its shape. Both operators report a position, not a verdict:

  • -1 — the left value comes first.
  • 0 — they belong in the same place.
  • 1 — the right value comes first.
Azvya Education Pvt. Ltd.VLSI Mentor
three-way.pl
use strict;
use warnings;

print "5 <=> 3     : ", (5 <=> 3), "\n";
print "3 <=> 5     : ", (3 <=> 5), "\n";
print "5 <=> 5     : ", (5 <=> 5), "\n";
print "'a' cmp 'b' : ", ("a" cmp "b"), "\n";
print "'b' cmp 'a' : ", ("b" cmp "a"), "\n";
print "'a' cmp 'a' : ", ("a" cmp "a"), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
5 <=> 3     : 1
3 <=> 5     : -1
5 <=> 5     : 0
'a' cmp 'b' : -1
'b' cmp 'a' : 1
'a' cmp 'a' : 0

The result is a sign, not a distance. This catches people arriving from C, where strcmp may return a difference:

Azvya Education Pvt. Ltd.VLSI Mentor
sign-not-distance.pl
use strict;
use warnings;

print "1 <=> 1000000 : ", (1 <=> 1000000), "\n";
print "1000000 <=> 1 : ", (1000000 <=> 1), "\n";
print "'a' cmp 'zzzz': ", ("a" cmp "zzzz"), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
1 <=> 1000000 : -1
1000000 <=> 1 : 1
'a' cmp 'zzzz': -1

A gap of one million gives the same -1 as a gap of one. Do not use the return value as a measure of how far apart two values are — it only tells you which way round they go.

2. The Same Split, the Opposite Answer

These two operators divide numeric from textual exactly as Chapter 3.5 and Chapter 3.6 did, and here the consequence fits on two lines:

Azvya Education Pvt. Ltd.VLSI Mentor
opposite.pl
use strict;
use warnings;

print "'10' cmp '9' : ", ("10" cmp "9"), "\n";
print "10 <=> 9     : ", (10 <=> 9), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
'10' cmp '9' : -1
10 <=> 9     : 1

Same two operands. Opposite signs. cmp says "10" comes first, which is correct text ordering — the character 1 precedes the character 9. <=> says 9 comes first, which is correct numeric ordering.

Since these operators exist mainly to drive sort, an opposite sign means a reversed list. That is Chapter 3.6 §3's default-sort trap seen from the comparator's side, and it is the reason the two chapters belong next to each other.

3. Why 0 Is the Useful Part

Returning 0 on a tie is what makes multi-key sorting work. The idiom is to try one key, and if it ties, fall through to another:

Azvya Education Pvt. Ltd.VLSI Mentor
two-key-sort.pl
use strict;
use warnings;

my @rows = (
    "alu 3",
    "fifo 1",
    "decoder 3",
    "alu 1",
);

for my $r (sort {
        my ($an, $ac) = split / /, $a;
        my ($bn, $bc) = split / /, $b;
        $bc <=> $ac || $an cmp $bn
    } @rows) {
    print "$r\n";
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
alu 3
decoder 3
alu 1
fifo 1

Failure count descending, and within an equal count, name ascending. alu 3 and decoder 3 tie on the count — $bc <=> $ac gives 0 — so the || moves on to $an cmp $bn, which puts alu before decoder.

That works because 0 is false in Perl, by Chapter 2.8, and -1 and 1 are both true. So "they tie" and "keep looking" are the same value. || and its short-circuit behaviour are Chapter 3.8's subject; it appears here because the multi-key sort is the reason the three-way return matters at all.

sort itself belongs to Module 6, which covers the $a and $b variables and split properly. What this chapter owns is the comparator in the middle.

4. <=> Can Return undef

Every other comparison operator in this module returns something you can treat as true or false. <=> has one input where it does not.

NaN — "not a number" — is a floating-point value that arises from an undefined operation. It is not an error and it does not stop the program:

Azvya Education Pvt. Ltd.VLSI Mentor
nan.pl
use strict;
use warnings;

my $nan = 9**9**9 - 9**9**9;

print "the value prints as : $nan\n";

my $r = ($nan <=> 1);
print "nan <=> 1 defined?  : ", (defined $r ? "yes ($r)" : "NO - undef"), "\n";

print "nan == 1            : ", ($nan == 1 ? "true" : "false"), "\n";
print "nan != 1            : ", ($nan != 1 ? "true" : "false"), "\n";
print "nan <  1            : ", ($nan <  1 ? "true" : "false"), "\n";
print "nan >  1            : ", ($nan >  1 ? "true" : "false"), "\n";
print "nan == nan          : ", ($nan == $nan ? "true" : "false"), "\n";

my $c = ($nan cmp 1);
print "nan cmp 1 defined?  : ", (defined $c ? "yes ($c)" : "NO - undef"), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
the value prints as : NaN
nan <=> 1 defined?  : NO - undef
nan == 1            : false
nan != 1            : true
nan <  1            : false
nan >  1            : false
nan == nan          : false
nan cmp 1 defined?  : yes (1)

Four things in that output are worth stopping on.

  • $nan <=> 1 is undef. Not 0, not -1, not 1. NaN has no position relative to 1, so the operator declines to give one. This is the distinction from Chapter 2.7 appearing where you would least expect it: an operator returning "no answer" rather than an answer.
  • < and > are both false. Normally exactly one of them is true for two different values. Here neither is, which breaks the reasoning "it is not less than, so it must be greater than or equal".
  • $nan == $nan is false. A value that is not equal to itself. Any code that assumes $x == $x holds — a cache check, a change detector, a convergence test — is wrong for this value.
  • cmp works fine and returns 1. cmp is a text comparison, and as text the value is the three characters NaN, which compare perfectly well against 1. That is not cmp being safer; it is cmp answering a different question.

What that does to a sort

Azvya Education Pvt. Ltd.VLSI Mentor
sort-with-nan.pl
use strict;
use warnings;

my $nan = 9**9**9 - 9**9**9;
my @values = (3, $nan, 1, 2);

print join(" ", sort { $a <=> $b } @values), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
1 2 3 NaN
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDERR
Use of uninitialized value in sort at sort-with-nan.pl line 7.
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — exit status
0

Where a NaN comes from in real work

You are unlikely to type 9**9**9 - 9**9**9. You are quite likely to read a field that a tool wrote as nan or NaN when a measurement was unavailable — and that text becomes a real NaN, without a warning:

Azvya Education Pvt. Ltd.VLSI Mentor
nan-from-a-log.pl
use strict;
use warnings;

for my $field ("nan", "NaN", "NAN", "-nan", "inf") {
    my $n = $field + 0;
    print "$field + 0 = $n\n";
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
nan + 0 = NaN
NaN + 0 = NaN
NAN + 0 = NaN
-nan + 0 = NaN
inf + 0 = Inf
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet

Nothing on STDERR. This is the one place Chapter 2.13's rule does not apply: most non-numeric text numifies to 0 and warns, but nan in any capitalisation is recognised as a genuine floating-point value and converts silently. inf likewise becomes Inf.

So a NaN enters your data as an ordinary-looking field from an ordinary-looking log line, with no diagnostic at the point of entry. The first sign of it is the sort warning above, a long way from the cause.

Note also that dividing by zero does not produce NaN in Perl — Chapter 3.2 §3 showed it is a fatal error. That is arguably the friendlier behaviour: it stops rather than poisoning a value that then sorts oddly three hundred lines later.

5. Why This Matters in Engineering Work

Report ordering is a correctness question. A failure summary sorted by count descending is read top-down and acted on from the top. The comparator decides which test gets looked at first, so cmp where <=> was meant does not merely untidy the report — it changes which work gets done.

Multi-key ordering is the normal case. "Worst slack first, then by instance name" is the shape of most useful engineering reports, and it depends entirely on the tie returning 0. Writing the comparator with < instead of <=> cannot express it: < has no way to say "same".

A NaN in your data is a silent reordering. The specific risk is not a crash, it is a list that looks right. One uninitialized-value warning from inside sort is the only evidence, and it names the sort line rather than the data that caused it. Checking a parsed number before you sort on it is the fix, and Module 8's conditionals give you the tools.

6. Common Mistakes

Reading the return value as a magnitude. It is -1, 0 or 1 regardless of the gap.

Using cmp on numbers in a comparator. "10" cmp "9" is -1 and 10 <=> 9 is 1, so the list comes out reversed where digit counts differ.

Assuming <=> always returns a number. With NaN it returns undef, and sort silently treats that as a tie.

Assuming exactly one of < and > must be true. For NaN both are false.

Assuming $x == $x. False for NaN.

Thinking cmp is the safe one because it survived NaN. It survived by answering a textual question. That is only correct if a textual answer is what you wanted.

Ignoring Use of uninitialized value in sort. It means your comparator returned nothing for at least one pair, and the resulting order is not trustworthy.

7. Exercise

Exercise 1 — the three answers. Run the §1 script and record all six values. Then state in one sentence what distinguishes these operators from < and lt.

Exercise 2 — sign, not distance. Compute 3 <=> 4 and 3 <=> 4000000. Record both and say what that rules out as a use for the return value.

Exercise 3 — the opposite sign. Predict "100" cmp "99" and 100 <=> 99 before running them. Then, using a four-element list of numbers with differing digit counts, sort it with each comparator and record the two orders.

Exercise 4 — the tie-break. Build a list of four strings of the form name count where two share a count, and sort by count descending then name ascending as in §3. Then delete the || $an cmp $bn part and record what changes about the two tied rows.

Exercise 5 — NaN, carefully. Reproduce the §4 script. Then sort a list containing the NaN and capture STDOUT and STDERR into separate files. Confirm that STDOUT alone gives no indication of a problem, and write one sentence on what that means for a nightly flow that logs only STDOUT.

Exercise 6 — the self-comparison. Write two lines that print whether $nan == $nan and whether $nan eq $nan. Explain the difference between the two results, naming the operator family each belongs to.

8. Summary

  • <=> and cmp return -1, 0 or 1 — a position, not a verdict, and not a distance.
  • <=> compares quantities; cmp compares characters. On 10 and 9 they return opposite signs.
  • 0 on a tie is the useful part, because 0 is false and so a || can fall through to a second key. That is how multi-key sorting is written.
  • <=> returns undef for NaN — the one comparison in this module whose answer is neither true nor false.
  • With NaN, < and > are both false, and $nan == $nan is false.
  • A sort over a NaN warns once and still produces a list. Use of uninitialized value in sort is the only signal, and the resulting order is an artefact.
  • cmp is not the safe alternative — it answers a textual question, which happens to be well-defined for the characters NaN.

Chapter 3.8 moves from comparing values to combining the answers, and starts with the property that makes || work as a tie-break at all: short-circuit evaluation.

Where this fits

Part of the Perl curriculum.