Perl comparison table

Perl has distinct operators == and eq for numerical and lexical comparisons respectively. An asterisk "*" indicates that a comparison raises a warning.

Note: unlike PHP and JavaScript, Perl does not have a true boolean, only false. When a comparison succeeds, 1 is returned. Also, false is not directly accessible.

Perl lacks the === "strict equality" operator.

Some variables

my $false = (0 == 1);
my @arr0 = ();
my %hash0 = ();
my @arr1 = ('perl');
my %hash1 = ('perl' => 'equality');

Numerical equality

==undef()''$false'perl'0'0'(0)('0')@arr0%hash01'1'(1)('1')@arr1%hash1
undeftrue**true**true**true*true**true*true*true*true*true*true*false*false*false*false*false*false**
()true**true**true**true*true**true*true*true*true*true*true*false*false*false*false*false*false**
''true**true**true**true*true**true*true*true*true*true*true*false*false*false*false*false*false**
$falsetrue*true*true*truetrue*truetruetruetruetruetruefalsefalsefalsefalsefalsefalse*
'perl'true**true**true**true*true**true*true*true*true*true*true*false*false*false*false*false*false**
0true*true*true*truetrue*truetruetruetruetruetruefalsefalsefalsefalsefalsefalse*
'0'true*true*true*truetrue*truetruetruetruetruetruefalsefalsefalsefalsefalsefalse*
(0)true*true*true*truetrue*truetruetruetruetruetruefalsefalsefalsefalsefalsefalse*
('0')true*true*true*truetrue*truetruetruetruetruetruefalsefalsefalsefalsefalsefalse*
@arr0true*true*true*truetrue*truetruetruetruetruetruefalsefalsefalsefalsefalsefalse*
%hash0true*true*true*truetrue*truetruetruetruetruetruefalsefalsefalsefalsefalsefalse*
1false*false*false*falsefalse*falsefalsefalsefalsefalsefalsetruetruetruetruetruetrue*
'1'false*false*false*falsefalse*falsefalsefalsefalsefalsefalsetruetruetruetruetruetrue*
(1)false*false*false*falsefalse*falsefalsefalsefalsefalsefalsetruetruetruetruetruetrue*
('1')false*false*false*falsefalse*falsefalsefalsefalsefalsefalsetruetruetruetruetruetrue*
@arr1false*false*false*falsefalse*falsefalsefalsefalsefalsefalsetruetruetruetruetruetrue*
%hash1false**false**false**false*false**false*false*false*false*false*false*true*true*true*true*true*true**

Lexical equality

equndef()''$false'perl'0'0'(0)('0')@arr0%hash01'1'(1)('1')@arr1%hash1
undeftrue**true**true*true*false*false*false*false*false*false*false*false*false*false*false*false*false*
()true**true**true*true*false*false*false*false*false*false*false*false*false*false*false*false*false*
''true*true*truetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalse
$falsetrue*true*truetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalse
'perl'false*false*falsefalsetruefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalse
0false*false*falsefalsefalsetruetruetruetruetruetruefalsefalsefalsefalsefalsefalse
'0'false*false*falsefalsefalsetruetruetruetruetruetruefalsefalsefalsefalsefalsefalse
(0)false*false*falsefalsefalsetruetruetruetruetruetruefalsefalsefalsefalsefalsefalse
('0')false*false*falsefalsefalsetruetruetruetruetruetruefalsefalsefalsefalsefalsefalse
@arr0false*false*falsefalsefalsetruetruetruetruetruetruefalsefalsefalsefalsefalsefalse
%hash0false*false*falsefalsefalsetruetruetruetruetruetruefalsefalsefalsefalsefalsefalse
1false*false*falsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruetruetruefalse
'1'false*false*falsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruetruetruefalse
(1)false*false*falsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruetruetruefalse
('1')false*false*falsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruetruetruefalse
@arr1false*false*falsefalsefalsefalsefalsefalsefalsefalsefalsetruetruetruetruetruefalse
%hash1false*false*falsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetrue

To catch warnings in Perl

local $SIG{'__WARN__'} = sub {
	my $warning = shift;
	# etc.
};

Why does the table look like this?

When Perl needs to interpret an expression as a scalar, such as for binary comparisons, arithmetic or string operations, it follows these rules:

  1. For a list expression such as (0), ('0'), (1) or ('1'), the last expression in the list is used.
  2. If the list is empty i.e. (), "the last expression in the list" is considered to be undef.
  3. For an array expression such as @arr0 or @arr1, the length of the array is used.
  4. For a hash expression such as %hash0 or %hash1, a string such as '1/8' indicating the number of filled buckets in the hash is used.

If number is required, as for both sides of ==, Perl follows these rules:

  1. Strings are coerced into numbers. '0' and '1' become 0 and 1 respectively. '1/8' becomes 1, with one warning. 'perl' and '' become 0, with one warning.
  2. References (not shown here) are coerced into an integer representing the location in memory where the referent is stored. E.g. [] might become 3309920.
  3. A scalar containing the false boolean, such as $false, becomes 0.
  4. undef becomes 0, with one warning.
  5. Now we have two numbers which can be compared in the usual fashion.

If a string is required, as for both sides of eq, Perl follows these rules:

  1. Numbers are written out as strings. 0 and 1 become '0' and '1' respectively. Positive infinity becomes '1.#INF', negative infinity becomes '-1.#INF', NaN becomes '-1.#IND'.
  2. References (not shown here) become a string representing the referent's type and location in memory. E.g. [] might become 'ARRAY(0x87b1d8)'.
  3. A scalar containing the false boolean, such as $false, becomes the empty string ''.
  4. undef becomes the empty string '', with one warning.
  5. Now we have two strings which can be compared in the usual fashion.

Observations

  • Unlike PHP and JavaScript, Perl expressions fall into equivalence classes; the operators == and eq are transitive.
  • A list expression, an array initialised from that list expression and a hash initialised from the same list expression can display radically different behaviour.

Further work

Expressions I am considering adding include ('perl'), ('perl' => 'equality'), [], {}, 1/8 and '1/8' (note that %hash1 eq '1/8', at least on my machine). It depends how useful this information would be.

Back to Code
Back to Things Of Interest

Discussion (36)

2014-09-07 03:31:04 by chridd:

Some other odd behavior not included in the table or the list in "Further work":
@arr2 = (3,4);
@arr3 = (0,0);
@arr2 eq @arr3 and @arr2 == @arr3 are true. (Neither equality operator compares the contents of lists.)
@arr2 eq (3,4) is false. @arr2 eq 2 is true; (3,4) eq 4 is true. Same with ==.
(This makes more sense if you understand that Perl interprets expressions differently depending on whether a list or scalar is expected: when a list is expected, (3,4) is a two-element list; when a scalar is expected, (3,4) behaves like C's comma operator and returns the last item. An array variable in a scalar context gives the length of the array. This also means that expressions like ('1') aren't actually lists, except where '1' would also be a list.)

('perl' => 'equality') eq 'equality' is true. This follows from the fact that => is mostly the same as a comma, and that commas in scalar contexts act like C's comma operator.

$ref0 = []; Suppose print $ref0 displays ARRAY(0x801b30).
Then $ref0 eq 'ARRAY(0x801b30)' is true and $ref0 == 'ARRAY(0x801b30)' is false. $ref0 == 0x801b30 is true. (That is, a reference converted to a number is not the same as a reference converted to a string and then converted to a number.) $ref0 eq [] and $ref0 == [] are both false ([] creates a new list, which is compared by reference).

defined(()) is false (as is $x = (); defined($x)). defined('') is true. For some reason, defined((1,2)) is an error (appears to be a compile-time error).

'123abc' == 123 is true. ' 123abc' == 123 is also true. 'abc123' == 123 is false; 'abc123' == 0 is true.

'0xa' == 0xa is false.

2014-09-07 04:23:05 by Rangi:

Can you add the string value "0 but true"? Since "0 but true" == 0 is true, and "0 but true" itself is true, even though 0 itself is false.

2014-09-07 05:37:51 by David Mitchell:

Dear God.
Not to self: never use perl.

2014-09-07 10:05:55 by Andrew:

Most of this is mistaken in some way.

The results of comparisons aren't 1 and '', they're the true and false constants. The true constant is indistinguishable from 1 without peeking at internals, but the false constant is 0 when asked to be a number and '' when asked to be a string.

The parentheses are all meaningless. Parentheses are nothing more or less than a grouping operator, they don't "make lists" or anything of the sort. The compiled code for (1) == (1) and for 1 == 1 are identical, just like the code for (2 * 3) + 4 and the code for 2 * 3 + 4 are identical. () is a slight exception, given that you can't write "0 =" and expect it to work, but "0 = ()" does.

References, which *should* be pointed out, are left out entirely. Two references compare equal (== and eq) if they point to the same thing, and unequal otherwise.

The cases with arrays and hashes aren't wrong, but they are misleading without explanation. It's intro-level stuff that an array gives the number of elements it contains in scalar context, thus @array > 10 tells you whether the array has more than 10 elements. == compares to this number, and eq compares to the string form of this number (which is useless, and no one does it). A hash gives the number of keys it contains when used as a number (which is sometimes, if rarely, useful), and a string indicating how many buckets are allocated and how many are used when used as a string (which is practically never useful, and never for eq).

2014-09-07 10:08:29 by Andrew:

(meant to say in the previous, but forgot to write — the point of the third paragraph is that removing the (0), ('0'), (1), and ('1') rows/columns would make things clearer without losing anything of value).

One that would be illustrative to add would be "1love", which == 1 but with a warning.

2014-09-07 10:41:37 by qntm:

Perl has false but not true? That is *amazing*, I added it.

2014-09-07 10:46:28 by Andrew:

The canonical ways to write true and false are !!1 and !1, of course :)

2014-09-07 17:17:17 by chridd:

> the false constant is 0 when asked to be a number and '' when asked to be a string.
...but the string '' is also 0 when asked to be a number, so it would seem to me that false would also be indistinguishable from '' without looking at the internals. Or is there some context where they are distinguishable?

2014-09-07 17:21:50 by qntm:

The string '' is 0 when asked to be a number, but it raises a warning when it does so. The false constant does not. This can be detected at run time using the code presented above for catching warnings.

Perl is *insane*.

2014-09-08 05:02:55 by Coda:

Less insane than PHP, at least. PHP is "Perl, by someone who doesn't understand Perl."

2014-09-08 06:55:19 by FKK:

That moment when you see an unvisited link on qntm, then realise it's about Perl and not Penamba...

2014-09-08 09:20:04 by qntm:

PHP is a minor evil perpetrated and created by incompetent amateurs, whereas Perl is a great and insidious evil perpetrated by skilled but perverted professionals.

2014-09-08 11:54:04 by Veky:

Perl is as insane as everything you're trying to fit into your preconceived notions about how things should work.

For comparison, meditate on the sentence "Perl uses + for addition of numbers, and . for addition of strings". :-P

The truth, of course, is that there are no numbers nor strings anywhere. There are scalars. And nonscalars, of course. :-)

2014-09-08 12:23:47 by qntm:

You say "preconceived notions", I say "violates the principle of least surprise".

2014-09-08 15:51:36 by Moses:

I've been using Perl for decades and I've never even heard of hashtables being coerced into strings like '1/8' before. Probably because I know `eq` is to be used only for comparing two string-type scalar vars, so I've never depended on it to compare two non-string or non-scalar vars.

Why would a programmer use an expression operator for a purpose contrary to it's definition, and not expect a surprise?

2014-09-08 16:24:45 by qntm:

Because it doesn't even throw a warning, let alone a compile-time error?

2014-09-08 18:30:10 by davidgro:

Where do the (various numbers of) asterisks in the table lead? There seem to be some missing footnotes that I would guess are about warnings.

2014-09-08 19:43:52 by Veky:

Perl never subscribed to that principle anyway. It went for the principle of most usefulness, and of course it sometimes clashes with POLS.

2014-09-09 00:38:01 by MichaelSzegedy:

@davidgro: The number of asterisks indicates the number of errors. Try mousing over the entries.

@Veky: Being surprising is in itself something that makes a language less useful.

2014-09-09 05:32:32 by chridd:

Oh, and also: "nan" == "nan" is false, as is "nanny" == "nanny" (same for "+nan", "-nan", and "nAn"). 0/0, however, is an error, as is sqrt(-1) and 0/1; (1e308+1e308)-(1e308+1e308) gives nan though. (1e308+1e308)-(1e308+1e308) eq (1e308+1e308)-(1e308+1e308) is true. I don't know how portable this is.

Also, your CAPTCHA thing doesn't accept "Can't take sqrt of -1" ☺.

2014-09-09 05:35:59 by chridd:

Oops. Meant 1/0, not 0/1.

2014-09-12 13:13:07 by MichaelSzegedy:

You might also want to include 1.0 vs 1. (Does Perl tell the difference between floats and ints? I wouldn't think so, but Perl has been surprising before.)

2014-09-14 15:54:18 by Tassos Bassoukos:

Sam, have you seen the *Perl 6* periodic table of operators?

2014-09-14 16:42:49 by qntm:

I have. It reads like an elaborate joke. I see Perl 6 has at least five equality operators now, including `eqv` for "canonical equivalence", `===` for "value identity" and `=:=` for "container identity".

2014-09-15 03:46:40 by Joel Berger:

You really should remove this: "If two expressions are lexically equal, they are always numerically equal too."

There are several cases where this simply isn't true, like dualvars (scalars which define a value when used as a number vs when used as a number) and objects which overload stringification and/or numification. That said as chridd already mentioned, strings which start with "nan", such as the name "Nancy", fail this test as well.

Suggesting as you do that the two comparisons might be equal in some cases is inviting newcomers to do so in certain cases, when in fact these programs now have subtle bugs.

------

As a different note, this chart kinda makes Perl look incredibly complex. If you were to remove those comparisons which emit warnings (using the wrong comparison operator is essentially like making a type error in other languages) the chart looks much more manageable.

2014-09-15 07:35:08 by qntm:

How do I create a dualvar?

2014-09-15 08:43:57 by Xaerxess:

Sam, you can create dualvar with `Scalar::Util` (it's in the core): https://metacpan.org/pod/Scalar::Util#dualvar. Read also `isdual` desctiption.

You also may want to create "simple" table, as Joel wrote above, with pragma ` use warnings FATAL => 'all';` enabled.

2014-09-15 10:59:42 by qntm:

> "nan" == "nan" is false

I can't verify this. The string 'nan' coerces into 0 for me, regardless of case.

2014-09-15 11:59:55 by Joel Berger:

perl -E 'say "Nan"=="Nan" ? "true" : "false"'

Also I made a typo in my original comment, "scalars which define a value when used as a number vs when used as a STRING", but it looks like people understood.

2014-09-15 12:32:03 by qntm:

C:\Users\admin>perl -e "print 'Nan'=='Nan' ? 'true' : 'false';"
true

2014-09-16 00:14:07 by Joel Berger:

Is it possible that Nan is equal to Nan on windows C runtimes? I assure you, it does not on linux.

2014-09-17 02:54:57 by OvermindDL1:

``` ksh
$ perl -e "print 'Nan'=='Nan' ? 'true' : 'false';"
true
```

This is on SCO UNIX. perl -version is v5.6.0 built for i486-pc-sco3.2v5.0, my work computer.

2014-09-17 05:16:01 by chridd:

My computer (where 'nan' != 'nan') is running Mac OS X, perl version 5.8.8 (I also have perl 5.12.5, which behaves the same).

According to http://www.perl.com/doc/FMTEYEWTK/is_numeric.html Perl uses atof to convert strings to numbers. I wonder if systems differ in whether atof recognizes NaN (or if some systems are case sensitive). As I mentioned earlier, (1e308+1e308)-(1e308+1e308) gives NaN; if that still compares unequal to itself (but 'nan'=='nan'), that would confirm my hypothesis.

2014-09-17 17:07:08 by OvermindDL1:

And from home
```
$ uname -a
Linux <myhostname> 3.13.0-35-generic #62-Ubuntu SMP Fri Aug 15 01:58:42 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
$ perl -e "print 'Nan'=='Nan' ? 'true' : 'false';"
false
```
So it does indeed appear to be different on different systems. Perl is such a 'stable' language it seems...

2015-07-17 00:15:21 by Sean:

On POSIX-ish systems, infinity and NaN are typically represented by "inf" (or "infinity") and "nan", respectively. The case is not necessarily consistent across different OSs. Behavior on overflow might not be consistent either; on Linux, I get "inf" on overflow, but apparently chridd got NaN on OS X.

On Windows, you get strings like "1.#IND" instead, which is presumably why the weird "nancy" != "nancy" behavior doesn't happen.

This is all C library (atof) stuff, rather than coming from Perl itself.

2015-10-14 02:29:19 by chridd:

(Didn't see that comment until now.)
> Behavior on overflow might not be consistent either; on Linux, I get "inf" on overflow, but apparently chridd got NaN on OS X.
What I did wasn't just overflow. I overflowed to get infinity, then subtracted infinity from infinity to get NaN.