|Just another Perl shrine|
Re: $array[ 'Infinity' ]by ysth (Canon)
|on Dec 17, 2007 at 03:32 UTC||Need Help??|
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. 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.