http://qs321.pair.com?node_id=657332

Sixtease has asked for the wisdom of the Perl Monks concerning the following question:

Hello friends,

this is more an expression of confusion and surprise than a question.

@a = (5 .. 10); print $a['Infinity'], "\n", $a[-Infinity], "\n"; #outputs 10 and 5

Is this documented anywhere?

use strict; use warnings; print "Just Another Perl Hacker\n";

Replies are listed 'Best First'.
Re: $array[ 'Infinity' ]
by ysth (Canon) on Dec 17, 2007 at 03:32 UTC
    There are some perl bugs here. Some are very highly likely to be never fixed in perl5, some only highly likely to never be fixed.

    First of all, just to be clear, perl 5.8.8 and later (as well as perl 5.6.2 and before, for some value of before) may treat a string that looks like a stringized infinite or NaN number as that number in numeric context. But only if the underlying libc provides support for that. (The 5.8.0 to 5.8.7 gap is due to perl moving away from using libc to convert strings to floating point, after the boneheaded decision by the ISO C folk to radically change the rules for how "0x..." and the like were converted. The inf/nan stuff was left out of perl's homegrown atof for that period.)

    The other special thing perl does is not give the "Argument "..." isn't numeric" warning when encountering these string values in numeric context. This is the case all through the 5.8.x series (and even in 5.8.8 on platforms where the libc doesn't translate "Inf" to Inf), even though the numeric value will be 0, just as if the string had been "xyzzy".

    Now one interesting and potentially dangerous thing when dealing with infinites in perl is that perl doesn't really have a separate "integer context". When you use something as an array index, it is in a general numeric context, and will get converted to a number. If it's an "Inf" string, that will be a NV containing Inf (where supported). But an array index is an integer; the aelem op that looks up an array element given an array and index blindly converts the NV to an IV (usually with range -2**31 .. 2**31-1 or -2**63 .. 2**63-1), and if the value was outside that range, uses the closest endpoint. So on a perl using 64 bit ints, the index will be -2**63 for -Inf or 2**63-1 for Inf. Now the fun part comes... The underlying array access routines expect the element to be a 32 bit int, so -2**63 is truncated (taking the least significant 32 bits) to 0 and 2**63-1 is truncated to -1.

    So with 64 bit ints and Inf string conversion support, $x[Inf] is $x[-1] and $x[-Inf] is $x[0]. But with 32 bit ints, $x[Inf] is $x[2**31-1] and $x[-Inf] is $x[-2**31], both of which are undef.

    The only problem is that even with a 32 bit int perl 5.8.8, I'm seeing $x[Inf] be 10 (which I've just logically deduced can't be the case) and haven't yet figured out why.

    Update: oh, right! I remember now. When an IV or UV is requested from an NV, the result is restricted to the range IVMIN to UVMAX, regardless of whether an unsigned or signed value was requested. So "Inf" is essentially cast to an NV, becoming Inf, then to a UV, becoming 2**32-1, then to an IV, becoming -1 (same bit pattern as the unsigned 2**32-1) when "Inf" is used as an array element and hence coerced to an IV. And $x[Inf] becomes $x[-1] whether on a 64 bit in or 32 bit int perl.

Re: $array[ 'Infinity' ]
by Sixtease (Friar) on Dec 16, 2007 at 23:28 UTC
    No, this is not caused by numerical equivalence to zero. Nor is it caused by Infinity/inf differences:
    @a = (5 .. 10); print $a['inf'], "\n", $a[-inf], "\n", -inf==0 ? "-inf is zero\n" : "-inf isn't zero\n", 'inf'==0 ? "inf is zero\n" : "inf isn't zero\n"; # still outputs 10 and 5 and states that the infinities aren't zeroes

    perl --version says "This is perl, v5.8.8 built for x86_64-linux-thread-multi"

    Update: The weird thing is that indexing by -inf gives the first element and indexing by inf gives the last one. That's not what I'd expect.

    use strict; use warnings; print "Just Another Perl Hacker\n";
      Using perl -MO=Deparse may shed some light on some of this weirdness, although I have no idea why on Earth anyone would implement 'inf' the way it works on my box.
      perl -MO=Deparse -e 'print $n[inf]'
      prints
      print $n[9**9**9];
      as does
      perl -MO=Deparse -e 'print $n["inf"]'

       

      On the other hand,

      perl -MO=Deparse -e 'print $n[-inf]'
      prints
      print $n[-'inf'];

       

      perl -MO=Deparse -e 'print "", inf==0 ? "a" : "b", 'inf'==0 ? "c" : "d +", -inf==0 ? "e" : "f"'
      prints
      print '', 'inf' == 0 ? 'a' : 'b', 'inf' == 0 ? 'c' : 'd', -'inf' == 0 +? 'e' : 'f';

      As nearly as I can tell at the moment, inf is only equivalent 9**9**9 if it is used by itself as a subscript; it is taken as a bareword in a simple assignment, so

      perl -MO=Deparse -e '$n[inf], $n[1+inf], $n[-inf] = inf'
      prints
      $n[9**9**9], $n[1 + 'inf'], $n[-'inf'] = 'inf';

       

      My perl -v shows,

      This is perl, v5.8.8 built for i486-linux-gnu-thread-multi

       

      Does anyone know why? - quester

        That is terrifyingly weird. But the 9**9**9 is just an artifact of B::Deparse, as can be seen here. I'm not sure why it does that.

        Then again, it makes a kind of twisted sense that $a[inf] means "last element of @a", and (completely by accident) $a[-inf] means "last element counting from the end."

        Oddly (or maybe not so oddly) I get different output on a w2k box from the first cited by quester:
        perl -MO=Deparse -e "print $n[inf]" print $n[0]; -e syntax OK
        from...
        perl -v This is perl, v5.8.8 built for MSWin32-x86-multi-thread (with 33 registered patches, see perl -V for more detail) ... Binary build 819 [267479] provided by ActiveState http://www.ActiveSta +te.com Built Aug 29 2006 12:42:41

        As to the "why", X = 9**9**9 == 9**(9**9) = 9**387420489. Nine to that power is a number aproximately 1.2 billion bits long. (log(9)/log(2) * (9**9)). So, for all intents and purposes, it's infinity. Since it's odd, -X will be negative infinity So, it avoids the (possible?) non-portability of a raw 'inf' or '-inf'.

        Update: Duh, Ben. Re: strikeout. Plus it's specifically negated. I was thinking (-9)**9**9)

      Interesting.  I can confirm your results for "v5.8.8 built for x86_64-linux-thread-multi".  For "v5.8.8 built for i486-linux-gnu-thread-multi", however, I'm just getting 10 for 'Infinity' (or inf) and nothing (undef) for -Infinity (or -inf).

Re: $array[ 'Infinity' ]
by shmem (Chancellor) on Dec 17, 2007 at 01:56 UTC
    Is this documented anywhere?
    Yes, in sv.c in the perl source ("use the source, luke" ;-)

    Line 1680 ff:

    /* =for apidoc looks_like_number Test if the content of an SV looks like a number (or is a number). C<Inf> and C<Infinity> are treated as numbers (so will not issue a non-numeric warning), even if your atof() doesn't grok them. =cut */

    and so - suprise! - perlapi tells the same.

    Read through sv.c to see what happens. The perl5 spec is coherent with its implementation ;-)

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: $array[ 'Infinity' ]
by moritz (Cardinal) on Dec 16, 2007 at 23:17 UTC
    It prints only 5\n\n for me (with perl 5.8.8).

    The $a['Infinity'] part is not surprising, because the string is coerced into an integer, which is 0.

    I was quite surprised though that -Infinity is evaluated as -inf (and $a[-inf] is undef; no surprises here). A quick grep through the pod files in my perl distribution show that Infinity was introduced in perl561delta as a synonym for inf.

    It's a bit weird that it dies under strict, IMHO.

      If you really want a strict version:
      use strict; my @a = (5 .. 10); print $a['Infinity'], "\n", $a['-Infinity'], "\n"; print $a['Infinity'], "\n", $a['-Infinity'], "\n"; print 0+"Infinity" . "\n"; print 0+"-Infinity" . "\n";
      You still get:
      10
      5
      10
      5
      inf
      -inf
      
      The version:
      perl -v
      This is perl, v5.8.8 built for cygwin-thread-multi-64int...
Re: $array[ 'Infinity' ]
by ikegami (Patriarch) on Dec 16, 2007 at 23:19 UTC

    -bareword is the same as "-bareword".

    >perl -e "use Devel::Peek; $x="-bareword"; Dump($x); $x=-bareword; Dum +p($x)" SV = PV(0x226150) at 0x226000 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x1835404 "-bareword"\0 CUR = 9 LEN = 12 SV = PV(0x226150) at 0x226000 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x1835404 "-bareword"\0 CUR = 9 LEN = 12

    There's nothing special about -Infinity.

    >perl -e "use Devel::Peek; $x="-Infinity"; Dump($x); $x=-Infinity; Dum +p($x)" SV = PV(0x226150) at 0x226000 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x1835404 "-Infinity"\0 CUR = 9 LEN = 12 SV = PV(0x226150) at 0x226000 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x1835404 "-Infinity"\0 CUR = 9 LEN = 12

    A string that doesn't look like a number is the same as 0 in a numerical context (although it will give a warning if warnings enabled).

    The index expression of an array is evaluated in a numerical context.

    So they both print 5.

    >perl -e"@a = (5 .. 10); print qq{$a['Infinity']\n$a[-Infinity]\n};" 5 5

    Update: There appears to be a bug. 'Infinity' and -Infinity don't trigger the warning.

Re: $array[ 'Infinity' ]
by Sixtease (Friar) on Dec 17, 2007 at 00:47 UTC

    Do you think that this should be filed as a bug? I mean the different behavior of v5.8.8 built for different platforms, not the confusion about Infinity and inf. This is in my opinion not a matter of atof because the problem arises after you manage to get the actual infinity. Array indexed by minus infinity shouldn't yield the first element and if so, then on all platforms, I would think. If you agree that it should be filed, then I humbly ask your guidance on how and where to do so.

    use strict; use warnings; print "Just Another Perl Hacker\n";
Re: $array[ 'Infinity' ]
by explorer (Chaplain) on Dec 16, 2007 at 23:12 UTC

    infinity is recognized as a number from perl 5.6.1.

    Updated:Sorry for the changes, blokhead.

      Update: explorer changed the content of the parent post without any indication, so now my reply no longer makes much sense. The parent post originally read that the string "Infinity" evalutes to zero in numeric context, thus 0+"Infinity" is 0, and -"Infinity" was -0.

      Did you test it? Besides, why would $arr[0] be different than $arr[-0]?

      $ perl -le 'print 0+(-Infinity)' -inf $ perl -le 'print 0+("Infinity")' inf $ perl -v This is perl, v5.8.8 built for i486-linux-gnu-thread-multi
      I'm not sure I ever considered using infinity as an array offset. Nor did I guess that 0+"Infinity" would actually be infinity. It is cute that both do reasonable things.

      blokhead

        Is Perl suppose to support Infinity as a number? If so, it has two bugs: it doesn't work, and it's not in the docs. It's not in the 5.10.0 docs either.

        It's not recognized by the parser:

        >perl -e "use Devel::Peek; $x="Infinity"; Dump($x); $x=Infinity; Dump( +$x)" SV = PV(0x22612c) at 0x226000 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x182cb54 "Infinity"\0 CUR = 8 LEN = 12 SV = PV(0x22612c) at 0x226000 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x182cb54 "Infinity"\0 CUR = 8 LEN = 12 >perl -e "use Devel::Peek; $x="-Infinity"; Dump($x); $x=-Infinity; Dum +p($x)" SV = PV(0x226150) at 0x226000 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x1835404 "-Infinity"\0 CUR = 9 LEN = 12 SV = PV(0x226150) at 0x226000 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x1835404 "-Infinity"\0 CUR = 9 LEN = 12

        I also get different output from the commands you issued.

        >perl -le "print 0+(-Infinity)" 0 >perl -le "print 0+('Infinity')" 0
        >perl -v This is perl, v5.8.8 built for MSWin32-x86-multi-thread (with 25 registered patches, see perl -V for more detail) Copyright 1987-2006, Larry Wall Binary build 817 [257965] provided by ActiveState http://www.ActiveSta +te.com Built Mar 20 2006 17:54:25 ...

        Has this been discussed on P5P?