Home > Uncategorized > Perl comparison operators: A cautionary tale

Perl comparison operators: A cautionary tale

Individuals who work with strongly types languages are sometimes surprised by the comparison operators in Perl.¬†¬† Perl actually has two sets of comparison operators – one for comparing numeric values and one for comparing string (ascii) values. Since comparison operators are typically used to control logical program flow and make important decisions, using the wrong operator for the value you are testing can lead to bizarre errors and hours of debugging if you’re not careful.

The simplest and probably most used comparison operators test to see if one value is equal to another value. If the values are equal, the test returns true and if the values are not equal, the test returns false. For testing the equality of two numeric values, we use the comparison operator ==. For testing the equality of two string values, we use the comparison operator eq (EQual).

if (2 == 2) { print “== for numeric values\n”; }
if (‘moe’ eq ‘moe’) { print “eq (EQual) for string values\n”; }

Testing for the opposite, not equal, is very similar. Remember that this test will return true if the values tested are not equal to each other. To see if two numeric values are not equal to each other, we use the comparison operator !=. To see if two string values are not equal to each other, we use the comparison operator ne (Not Equal).

if (5 != 6) { print “!= for numeric values\n”; }
if (‘apples’ ne ‘oranges) { print “ne (Not Equal) for string values\n”; }

Bad things happen when you use a variable (containing a number) for output at some point, and also compare the contained number within to predined flows to support program flow decision. For example:

$number = 5;
if($number == 5){
print “the number is $number\n”;
}

The comparison will fail in many cases (when Perl assumes that the $number contains a string).

Edit: It will not fail in this instance, but knowing that this can happen can help you resolve a very tricky bug.

To protect yourself  from this type of failure, it makes sense to explicity inform Perl that $number contains a number. The way you do this is by assigning floating point numbers to the variable, and carrying out all comparison with floating point numbers. Consider the following example:

$number = 5.5;
if($number == 5.5){
print “the number is $number\n”;
}

Tags: ,
  1. February 20th, 2009 at 00:16 | #1

    Uhh, what? That’s not true at all. $number == 5 is safe whether $number = 5 or $number = “5”.

    To understand why it’s important to know that Perl stores both the integer and string value and uses each appropriately. Devel::Peek sheds some light on this.

    $ perl -wlE ‘use Devel::Peek; $number = 5; Dump($number)’
    SV = IV(0x81248c) at 0x812490
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 5

    IV contains the integer value. Now look at $number = “5”.

    $ perl -wlE ‘use Devel::Peek; $number = “5”; Dump($number)’
    SV = PV(0x801838) at 0x812490
    REFCNT = 1
    FLAGS = (POK,pPOK)
    PV = 0x202000 “5”
    CUR = 1
    LEN = 4

    PV contains the string value. Now look at this:

    perl -wlE ‘use Devel::Peek; $number = “5”; say “Ok” if $number == 5; Dump($number)’
    Ok
    SV = PVIV(0x803804) at 0x812490
    REFCNT = 1
    FLAGS = (IOK,POK,pIOK,pPOK)
    IV = 5
    PV = 0x202010 “5”
    CUR = 1
    LEN = 4

    Now it contains both a PV and an IV. Perl computes the integer value from the string value when it needs to, in this case at the point when we say $number == 5.

    It’s floating point comparisons that are dangerous! Here’s a simple example.

    $ perl -wlE ‘use Devel::Peek; $number = .001 / 126; $new_number += $number for 1..126; Dump $new_number’
    SV = NV(0x815c10) at 0x8124d0
    REFCNT = 1
    FLAGS = (NOK,pNOK)
    NV = 0.000999999999999999

    Equivalent mathematical operations, different results. This is not a problem unique to Perl. To understand why, you must understand how computers store numbers.
    https://secure.wikimedia.org/wikipedia/en/wiki/Floating_point_numbers#Accuracy_problems

  2. February 20th, 2009 at 07:04 | #2

    I’m confused. This comparison does work:

    % perl -le ‘$number = 5; print “the number is $number” if $number == 5;’
    the number is 5

    What am I missing/

  3. February 20th, 2009 at 14:37 | #3

    @Schwern thank you for this informative comment. I agree with you the fp is dangerous, but in most cases when you’re simply setting a flag, they should work fine.

    @schwern and @Theory. I agree with you both that this comparison will work, even though I indicated that it would not. I’ve updated the post to state that this is a made-up example.

    Perl normally does do ‘the right thing’; however in my experience, this bug does exist. It’s cost me many hours of my life on two occasions.

    I do not know the reason why, and will investigate more into it, but it may be some sort of interpreter optimization that is behaving erratically. The floating point work-around has resolved this problem for me.

    I’ll dig up the actual code in which this problem arose and post it here, and we can study it further.

  4. May 12th, 2009 at 00:36 | #4

    Hi Jessica,
    You’re welcome to link to this content, and are free to copy this to your own site as well. Knowledge is meant to be useful to everyone. The more it is shared, the more useful it becomes.

  1. No trackbacks yet.