http://www.perlmonks.org?node_id=1185615

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

Just curious, not a 'problem' which requires solution. Maybe developers thought real numbers stringification is usually approximate, that's why? Or simply nobody cares?

C:\>perl -MDevel::Peek -e "$x = 0.5; qq/$x/; Dump $x" SV = PVNV(0x38bb4) at 0xd217cc REFCNT = 1 FLAGS = (NOK,pNOK) IV = 0 NV = 0.5 PV = 0xd16014 "0.5"\0 CUR = 3 LEN = 28 C:\>perl -MDevel::Peek -e "$x = 0; qq/$x/; Dump $x" SV = PVIV(0xd200c4) at 0xd2685c REFCNT = 1 FLAGS = (IOK,POK,pIOK,pPOK) IV = 0 PV = 0xd16274 "0"\0 CUR = 1 LEN = 10

Hence, 'double' is converted to string any time it is required. E.g.:

use Benchmark qw/ cmpthese /; $x = 42.0; %h = (); cmpthese( -1, { F => sub { $h{ pack 'F', $x } = 1 }, s => sub { $h{ $x } = 1 }, });
Rate s F s 344025/s -- -86% F 2490475/s 624% --

I found that one my little application, which maintains kind of 'seen' hash, gets nice boost if hash keys are packed as above, instead of 'just used as they are'. Actually, keys are pixels coordinates, they became real numbers because library I use returns them so. Pixels are usually aplenty and hash is accessed a lot, therefore speed gain was significant. But of course it would be even faster if PV was added on first access and POK was set, i.e. without packing.

Replies are listed 'Best First'.
Re: Any reason NV is not marked as POK when accessed as string?
by dave_the_m (Monsignor) on Mar 23, 2017 at 11:56 UTC
    Ths stringification of a floating-point number is affected by locale (e.g. '.' verses ',') so the same NV variable could stringify to different strings at different times. Hence the PV value isn't cached.

    Dave.

      What you say makes sense, but makes me wonder why we upgrade NV's to PVNV's, and makes me think maybe we used to trust the cached value... Which makes me wonder if we couldn't change the logic so that when we stringify NV's or PVNV's we check the locale, and /then/ decide to upgrade/trust the cached value. IOW, why are paying a price for a locale switch when there is a decent chance most operations will be under a common locale? Is it expensive to check locale? (I will do some archive digging to see if these answers are in my mailbox, but I thought i would ask anyway).

      ---
      $world=~s/war/peace/g

        ... makes me think maybe we used to trust the cached value

        I think https://www.nntp.perl.org/group/perl.perl5.porters/2016/10/msg240576.html and responses is relevant here.
        As also mentioned there, the stringification of the NV is retained if the NV is an inf or nan.
        I assumed this was ok because stringifications of inf and nan were not affected by locale. (But I notice that stringification of 0.0 to "0" is not retained.)

        Cheers,
        Rob

      Ah, makes sense. Thank you for explanation.

Re: Any reason NV is not marked as POK when accessed as string? (updated)
by haukex (Archbishop) on Mar 23, 2017 at 12:11 UTC

    You could also force stringification to get an even bigger speedup compared to calling pack on every hash access (if that's what you're doing), e.g. $x = "$x" or $_="$_" for @values;. Update: Added the pre-packed test with $p, which surprisingly is still not as fast as the stringified float $y. Update 2: By giving each test case its own hash instead of using one hash for all tests, now the test cases "y" and "p" perform roughly the same, as I would have originally expected (Update 3: However, across multiple runs of this benchmark, "y" still tends to outperform "p", often significantly). Update 4: If you set $x = 42.123456789, then "p" consistently outperforms "y", so I'd guess it has to do with the hash key length. (last update, I promise ;-) )

    use Benchmark qw/ cmpthese /; my $x = 42.0; my $y = "$x"; my $p = pack 'F', $x; my (%h1,%h2,%h3,%h4); cmpthese( -1, { F => sub { $h1{ pack 'F', $x } = 1 }, s => sub { $h2{ $x } = 1 }, y => sub { $h3{ $y } = 1 }, p => sub { $h4{ $p } = 1 }, }); __END__ Rate s F p y s 4032984/s -- -64% -84% -84% F 11327209/s 181% -- -54% -56% p 24707825/s 513% 118% -- -5% y 25997751/s 545% 130% 5% --

      Maybe I'll int the whole structure from orbit since they are integer pixels anyway.

      BTW, it's curios, too: int not only returns value, but marks its argument variable as 'IOK' if it 'looks like integer', even when called in void context. Therefore subsequent access as string caches PV. Clever Perl.

      C:\>perl -MDevel::Peek -e "$x=42.0; int $x; qq/$x/; Dump $x" SV = PVNV(0x38bb4) at 0xd2227c REFCNT = 1 FLAGS = (IOK,NOK,POK,pIOK,pNOK,pPOK) IV = 42 NV = 42 PV = 0xd162ac "42"\0 CUR = 2 LEN = 10