Perl Part I · Module 3 · Operators and Expressions
The Comparison Operators in Full
Ordering splits into two families exactly as equality did: angle brackets for quantities, two-letter words for text. The rule is the same, so the hard part is not learning it — it is noticing when you have got it wrong. Compare 10 and 9 as text and 10 comes first, because the character 1 precedes the character 9, and that is correct text ordering. Perl does not warn about it, because nothing odd happened: two strings were compared as strings. That silence is what makes this chapter different from the last one. Perl's default sort is also a text sort, which is the single most common place this behaviour surfaces, usually in a report where the rows are in a believable but wrong order.
Foundation14 min readPerlPart IFundamentalsComparisonSortingOperators
Part I · Module 3 · Chapter 3.6 · The Comparison Operators in Full
1. The Two Families Again
| Compare as | Less | Less or equal | Greater | Greater or equal |
|---|---|---|---|---|
| numbers | < | <= | > | >= |
| text | lt | le | gt | ge |
The split is the one Chapter 3.5 established, and for the same reason: a Perl scalar has no type for the operator to inspect, so the operator must say what kind of comparison is meant.
Each operator asks a different question. $a < $b asks is this quantity smaller? $a lt $b asks does this text come first in character order? Both are legitimate questions. Only one of them is usually the one you want.
2. As Text, 10 Comes Before 9
use strict;
use warnings;
print "10 < 9 : ", (10 < 9 ? "true" : "false"), "\n";
print "'10' lt '9' : ", ("10" lt "9" ? "true" : "false"), "\n";
print "'9' lt '10' : ", ("9" lt "10" ? "true" : "false"), "\n";10 < 9 : false
'10' lt '9' : true
'9' lt '10' : falseTen is not less than nine, and "10" is lt "9".
Text comparison works character by character from the left. The first character of "10" is 1; the first character of "9" is 9. In character order 1 comes before 9, the comparison is decided right there, and the second character of "10" is never consulted. Length does not enter into it.
Note what is not on STDERR: nothing. This is the crucial difference from Chapter 3.5. When you used == on two names, Perl warned twice — it had been asked for a numeric reading of something that was not numeric, and it said so. Here, lt was given two strings and compared them as strings. Nothing unusual happened from Perl's point of view, so there is nothing to report.
3. Default sort Is a Text Sort
This is where most people meet the behaviour, usually in a report.
use strict;
use warnings;
my @cycles = (10, 9, 100, 2);
print "default : ", join(" ", sort @cycles), "\n";
print "numeric : ", join(" ", sort { $a <=> $b } @cycles), "\n";default : 10 100 2 9
numeric : 2 9 10 100sort with no instructions sorts as text. 10 100 2 9 is correct character ordering and is not what anyone wants from a list of cycle counts.
sort belongs to Module 6 and the <=> operator to Chapter 3.7; this example is here because the default is a trap that lives in this chapter's subject matter. The one thing to carry away: if you are sorting numbers, you must say so. The bare sort is a text sort even when every element is a number, and the result is plausible enough to ship.
4. Character Order Is Not Alphabetical Order
Text comparison uses the characters' underlying code points, and for plain ASCII that means all uppercase letters come before all lowercase ones.
use strict;
use warnings;
print "'Z' lt 'a' : ", ("Z" lt "a" ? "true" : "false"), "\n";
print "'a' lt 'B' : ", ("a" lt "B" ? "true" : "false"), "\n";'Z' lt 'a' : true
'a' lt 'B' : false"Z" comes before "a", and "a" does not come before "B". A list of block names mixing cases — alu, Decoder, fifo, ALU_top — therefore sorts into groups by case rather than into the order a human would read as alphabetical.
That is correct character ordering and it is rarely the intent. Making a sort case-insensitive needs a function to normalise the case first, which is Module 4's and Module 6's territory. For now the useful knowledge is that "alphabetical" and "character order" are different things, and lt gives you the second.
5. The One Warning You Do Get
There is a case where ordering warns — the same case as in Chapter 3.5, where a numeric operator is handed text that is not numeric:
use strict;
use warnings;
my $tool = "dc_shell";
print "tool < 1 : ", ($tool < 1 ? "true" : "false"), "\n";tool < 1 : trueArgument "dc_shell" isn't numeric in numeric lt (<) at numeric-on-text.pl line 6."dc_shell" numifies to 0, and 0 < 1 is true. So a numeric ordering test on a tool name reports that the name is "less than one", which is meaningless but perfectly true.
One oddity in that message worth knowing. Perl calls < by the name numeric lt. If you grep your source for lt because the warning mentioned it, you will not find the line — the operator you wrote is <. The pattern holds across the whole family:
| You wrote | The warning says |
|---|---|
< | numeric lt |
<= | numeric le |
> | numeric gt |
>= | numeric ge |
== | numeric eq |
!= | numeric ne |
The word in the message is Perl's internal name for the operation, not the symbol you typed. Grep for the symbol.
And one more thing about that warning, which matters when you are counting them. A scalar warns on the first occasion its text is read as a number, and not again — the numeric reading is cached on the value:
use strict;
use warnings;
my $tool = "dc_shell";
my $a = ($tool > 1);
my $b = ($tool < 1);Argument "dc_shell" isn't numeric in numeric gt (>) at warns-once.pl line 6.Two bad comparisons, one warning, and it names only line 6. So the number of warnings is not the number of places you have the problem, and fixing the line Perl named can make the diagnostic disappear while the second mistake stays exactly where it was. Treat such a warning as "there is at least one of these", and read the surrounding code rather than only the line cited.
6. Comparisons Chain, Since Perl 5.32
Perl lets you write a range test the way mathematics does:
use strict;
use warnings;
my $cycles = 42;
print "in range\n" if 0 < $cycles < 100;in range0 < $cycles < 100 behaves as 0 < $cycles && $cycles < 100. The middle expression is evaluated once, not twice, and if the first comparison fails the second is not evaluated at all.
This is a Perl 5.32 feature, documented under "Chained comparisons capability" in that release's change notes. perldoc perlop's precedence table marks the family accordingly:
chained < > <= >= lt gt le ge
chain/na == != eq ne <=> cmp ~~Before 5.32 the same line was grouped left to right, so 0 < 5 < 2 was (0 < 5) < 2, which is 1 < 2 — true, where chained evaluation gives false:
use strict;
use warnings;
print "0 < 5 < 2 : ", (0 < 5 < 2 ? "true" : "false"), "\n";0 < 5 < 2 : falseNote also, from the table above, that the equality operators sit on a different precedence line. They do not chain together with < and friends, so a line mixing the two families is not doing what a chain would do.
7. Why This Matters in Engineering Work
A report in a believable wrong order. The default sort on test indices, cycle counts or error counts produces 1 10 100 11 2 — a sequence that looks sorted at a glance and puts the largest value third. Nobody reading the report has a reason to doubt it.
A threshold test that is off by a digit. A parsed slack value compared with gt instead of > compares text. For values that happen to have the same number of digits it gives the right answer, which is the worst possible behaviour: it works during development on tidy data and fails the first time a value crosses a digit boundary. There is no warning on any of those runs.
A version check. Comparing tool versions as text puts 9 after 10, so a check for "at least version 9" passes on version 10 only by accident of how the string compares. Version comparison is genuinely harder than it looks and is not a job for either family of these operators on its own.
The habit is the one from Chapter 3.5, applied to a case where Perl will not help you: decide what the value is at the moment you read it in, and pick the operator from that. For ordering there is no diagnostic to fall back on.
8. Common Mistakes
Using lt/gt on numbers. "10" lt "9" is true. The comparison is character by character and stops at the first difference.
Relying on a bare sort for numbers. It is a text sort. Say sort { $a <=> $b }.
Expecting a warning. Text comparison of text never warns, whatever the values mean. Only a numeric operator on non-numeric text warns.
Reading numeric lt in a warning as lt. That is Perl's name for <. Grep for the symbol, not the word.
Assuming character order is alphabetical order. All uppercase sorts before all lowercase.
Using a chained comparison in a portable script. It needs Perl 5.32 or newer, and older Perls parse the same line differently. Use &&.
9. Exercise
Exercise 1 — reproduce the inversion. Run the §2 script and record all three results plus an empty STDERR. Then write one sentence explaining why "10" lt "9" is true without using the word "wrong".
Exercise 2 — sort it twice. Build a list of the numbers 1, 2, 9, 10, 11 and 100 and print it sorted both ways, as in §3. Say which output you would put in a report and what a reader would conclude from the other one.
Exercise 3 — the silent threshold. Write a script with my $slack = "9"; and my $limit = "10"; that tests $slack > $limit and $slack gt $limit and prints both results. Capture STDERR and confirm it is empty. State what that tells you about catching this class of bug with warnings.
Exercise 4 — case ordering. Sort the list ("alu", "Decoder", "fifo", "ALU_top") with a bare sort and record the order. Explain the grouping you see, and say what would have to change to get the order a human would call alphabetical.
Exercise 5 — pick the family. For each pair, say which operator family you would use and why in one clause: (a) two cycle counts parsed from a log; (b) two test names being put in report order; (c) a measured temperature against a limit; (d) two revision strings that must sort the way the tool wrote them; (e) a zero-padded die index against a bound.
Exercise 6 — make the range test portable. Rewrite if (0 < $n < 100) without a chained comparison, and say in one sentence why that version is preferable in a script you do not control the Perl for.
10. Summary
< <= > >=order quantities;lt le gt georder text. Same split as equality, same reason.- As text,
"10"is less than"9"— comparison runs character by character from the left and stops at the first difference. - Text comparison never warns. The ordering mistake is silent, unlike the
==mistake, so the operator choice has to be deliberate. - A bare
sortis a text sort, even on a list of numbers.sort { $a <=> $b }is the numeric form. - Character order is not alphabetical order — all uppercase precedes all lowercase.
- A numeric operator on non-numeric text does warn, and Perl calls
<by the namenumeric ltin that message. - Comparisons chain as of Perl 5.32, and older Perls parse the same line as a left-to-right grouping. Use
&&for portability.
Chapter 3.7 covers the two operators that do not answer yes or no, but instead report which of two values comes first — and the one input for which one of them returns nothing at all.
Where this fits
Part of the Perl curriculum.
