Home > Blockchain >  Why is 00 equal to 0 in perl?
Why is 00 equal to 0 in perl?

Time:12-28

I am not a heavy perl user. I am just learning. However, recently I am working with an old script written by someone else. Essentially, the script reads an input file from which the variable "index_a" is defined such that when doing a simple print:

print "$index_a";
00

My confusion comes from the following:

When a comparison to "zero" is made, it holds true:

if ($index_a == 0) { print "Found first index: 0" };
Found first index: 0

I was expecting the comparison to be false. Since the value of index_a = 00 is obtained from reading a line from an input file. Hence to my understanding index_a is a string which value is "00", therefore the if statement should not be executed.

Could some perl expert help me understand this? Also, could you let me know what is the proper way to check for data types in perl? I have experience with python and C and hence this is behavior is very confusing to me.

Thanks!

CodePudding user response:

In Perl, the context is important when considering the value. A scalar variable can contain all sorts of data types, but what you are testing for determines what you find. For example:

if ("00" == 0)      # string "00" is numerically identical to 0
if ("a"  == 0)      # string "a" is numerically identical to 0 (gives a warning)
                    # Argument "a" isn't numeric in numeric eq (==)
if ("00" eq "0")    # string "00" is not stringwise identical to "0".
if (@a == 0)        # if the array @a is empty (length 0)

In a numerical equality check with a string, Perl will try to cast the string to a number. If the string begins with a number, it will be cast to that number (for the duration of the test), else it will be cast to 0.

You have to (sort of) know what you have in a variable when you test what it contains. Or else you have to check more thoroughly. In your case, when reading from a file, you might want to use eq to test for string equality.

CodePudding user response:

When you use a Perl string in a numeric context, such as a numeric comparison, it recognizes the starting part that looks like a decimal number and ignores the stuff after that. Whatever it recognizes becomes the number.

It follows these basic rules:

  • Ignore leading whitespace. This is handy when you extract a field out of columnar data and the number doesn't take up the entire column.
  • Allow for a single leading sign ( or -)
  • Skip leading zeros (so, no octal)
  • Capture decimal ASCII digits (0,1,2,3,4,5,6,7,8,9), allowing for a single decimal point (so, no semantic versioning numbers)
  • Stop at the first non-decimal-digit character
  • Whatever you have so far is the number. If you have nothing, the number is 0.

There are some things to note, though:

  • Adding 0 to a string is a common way to force numeric mode.
  • Underscores in strings are just underscores. You can use underscores to separate digits in number literals but not in strings. So, 123_456 is 123456 because the perl parser handles the _, but '123_456' is just 123.
  • However, if the first characters (excluding the sign) are "Inf", in any case, the string converts to "Inf" and includes the - sign but not the sign. This is the special IEEE value of Infinity.
  • However, if the first characters (excluding the sign) are "NaN", in any case, the string converts to "Nan" and excludes either sign. This is the special "Not a number" value.

Here are some examples:

#!perl
use v5.10;
use utf8;
use open qw(:std :utf8);

my @strings = qw(
    -5  7  006 -01 00
     .1234 -.9876 .657 ..890 1.2.3
    ١٢٣
    -    -657
    12a34 a987 123fred 0456barney
    0x12 12_34 0177
    NaN -NaN  NAN
    Inf  Inf -Inf INF Infamy Infinity
    );

push @strings, "   432", "   123", "  -987", "   063","   NaN", "   Inf", '';

foreach my $string ( @strings ) {
    printf "%-12s => %s\n", qq('$string'), 0   $string;
    }

And the output:

'-5'         => -5
' 7'         => 7
' 006'       => 6
'-01'        => -1
'00'         => 0
' .1234'     => 0.1234
'-.9876'     => -0.9876
'.657'       => 0.657
'..890'      => 0
'1.2.3'      => 1.2
'١٢٣'        => 0
'-'          => 0
' '          => 0
' -657'      => 0
'12a34'      => 12
'a987'       => 0
'123fred'    => 123
'0456barney' => 456
'0x12'       => 0
'12_34'      => 12
'0177'       => 177
'NaN'        => NaN
'-NaN'       => NaN
'NAN'        => NaN
'Inf'        => Inf
' Inf'       => Inf
'-Inf'       => -Inf
'INF'        => Inf
'Infamy'     => Inf
'Infinity'   => Inf
'   432'     => 432
'   123'     => 0
'  -987'     => -987
'   063'     => 63
'   NaN'     => NaN
'   Inf'     => Inf
''           => 0

Finally, there's another interesting thing about Perl scalars. When you use a string as a number, Perl converts the string to a number and remembers the conversion. It does not, however, change the string. Devel::Peek shows you the innards of Perl data structures.

#!perl
use v5.10;
use Devel::Peek;

select(STDERR); $|  ; # just to order the output from Dump

my $string = '123fred';
say "string is <$string>";
Dump( $string );

say '-' x 40;

my $n = $string   0;
say "string is <$string>";
Dump( $string );

Here's the output. At first, $string has a PV (pointer value) for a string. After the numeric operation, it also has IV and NV values for numbers.

string is <123fred>
SV = PV(0x7f8be980cab0) at 0x7f8bea0160f8
  REFCNT = 1
  FLAGS = (POK,IsCOW,pPOK)
  PV = 0x7f8be9610ca0 "123fred"\0
  CUR = 7
  LEN = 10
  COW_REFCNT = 1
----------------------------------------
string is <123fred>
SV = PVNV(0x7f8be980ac50) at 0x7f8bea0160f8
  REFCNT = 1
  FLAGS = (POK,IsCOW,pIOK,pNOK,pPOK)
  IV = 123
  NV = 123
  PV = 0x7f8be9610ca0 "123fred"\0
  CUR = 7
  LEN = 10
  COW_REFCNT = 1
  •  Tags:  
  • perl
  • Related