Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Re: Converting to number doesn't always work...

by AnomalousMonk (Archbishop)
on Nov 22, 2019 at 20:35 UTC ( [id://11109080]=note: print w/replies, xml ) Need Help??


in reply to Converting to number doesn't always work...

Consideration of the way Perl converts a string to a number is interesting, but I was puzzled by why all the other things that are done to $N in TEST() seem to have no effect. Here's the narrative I've imagined for the critical part of what's happening:

  1. In the  $N += 0; statement, the number 0 is added to the string $N (after its conversion to a temporary number according to the rules already discussed in this thread) to generate a number that is assigned to a temporary, intermediate variable, but not yet to $N.
  2. In the course of numifying the string and adding, the "... isn't numeric in addition ..." warning is triggered, and this is trapped by the  local $SIG{__WARN__} = sub { ... } handler temporarily installed by TEST().
  3. The  $SIG{__WARN__} handler assigns a lot of stuff to $N and prints a lot of stuff, but none of it makes any difference because...
  4. When the  $N += 0; statement completes, it assigns the contents of its temporary, intermediate variable to $N and that's all, folks; any changes to $N prior to this final assignment are wiped out.
Here's the code I used to convince myself of the validity of this narrative:
c:\@Work\Perl\monks>perl use strict; use warnings; sub TEST { defined $_[0] or return 0; my $N = shift; printf "TEST('$N') called - "; local $SIG{__WARN__} = sub { # doing anything or nothing to $N has no effect here # return; # try this printf "(in warn '$N' -> "; $N =~ tr|0-9||cd; $N = "0$N"; $N = 'garbage'; printf "'$N') - "; }; printf "\$N is '$N' before += - "; $N += 0; printf "\$N is '$N' after += \n"; return int($N); } for my $n (qw(55 55x x55)) { my $m = TEST($n); print "TEST('$n') returns $m \n\n"; } __END__ TEST('55') called - $N is '55' before += - $N is '55' after += TEST('55') returns 55 TEST('55x') called - $N is '55x' before += - (in warn '55x' -> 'garbag +e') - $N is '55' after += TEST('55x') returns 55 TEST('x55') called - $N is 'x55' before += - (in warn 'x55' -> 'garbag +e') - $N is '0' after += TEST('x55') returns 0
I guess an opcode decompilation could confirm my conjecture, but I'm too lazy to do this right now.

Update: Re-writing the  $N += 0; statement as  $N = $N + 0; may allow better visualization of what I think happens: The  $N + 0 expression must be evaluated first, and it's in this evaluation that the warning is triggered and a bunch of irrelevant things are done to $N; then the  $N = ...; assignment is done, overwriting any changes to $N made by the  $SIG{__WARN__} handler.


Give a man a fish:  <%-{-{-{-<

Replies are listed 'Best First'.
Re^2: Converting to number doesn't always work...
by harangzsolt33 (Chaplain) on Nov 22, 2019 at 22:26 UTC
    Yes, this is how it SHOULD work, but it doesn't! Let me simplify this script a little bit to demonstrate the problem. Run this script below. It should say TEST return value = 77 (at least it does on my computer which is Microsoft Windows 7 running TinyPerl 5.8.) But then you change the "return $N;" to "return $N + 0;" (and remember, at this point, $N is 77, so adding zero to it should leave it the same. But instead of returning 77, it flips its value BACK TO THE ORIGINAL!! which is totally WEIRD behavior! The return value should be 77, not 55.
    #!/usr/bin/perl -w use strict; use warnings; my @TEST_VALUES = ('55a', 'a55'); foreach my $i (@TEST_VALUES) { print "Calling TEST('$i'): "; print "\nTEST return value = ", TEST($i), "\n\n\n"; } exit; ####################################### sub TEST { my $N = shift; local $SIG{__WARN__} = sub { print "\nWarning was triggered. N=$N "; + $N = 77; print " Now N is $N **END OF WARNING**\n"; }; print "\n N=$N N+0= "; print "ADDITION: ", $N + 0; print "\n N=$N "; return $N; }

    I think, I found a BUG in the Perl interpreter. Lol ;)

      Here's something to ponder:

      c:\@Work\Perl\monks>perl -wMstrict -e "my @TEST_VALUES = ('55a', 'a55'); foreach my $i (@TEST_VALUES) { print qq{Calling TEST('$i') / }; print qq{TEST return values = @{[ TEST($i) ]} \n\n}; } ;; sub TEST { my $N = shift; local $SIG{__WARN__} = sub { print qq{(warn: N=$N -> }; $N = 77; print qq{N=$N) / }; }; print qq{N=$N / N+0=}, $N + 0; print qq{ / N.''=}, $N.'', qq{\n}; return $N, $N+0, $N.''; } " Calling TEST('55a') / (warn: N=55a -> N=77) / N=55a / N+0=55 / N.''=77 TEST return values = 77 55 77 Calling TEST('a55') / (warn: N=a55 -> N=77) / N=a55 / N+0=0 / N.''=77 TEST return values = 77 0 77
      Don't ask me what this means or if it's a bug (it's late and I need to think about this with a clear head), but I suspect it has something to do with the inherent dualvar nature of scalars (see Scalar::Util::dualvar): a scalar caches a string or numeric representation of its value if it was ever used as a string or number, and both representations may be present in the scalar at the same time and may represent entirely unrelated values! Runs the same under ActiveState 5.8.9 and Strawberry 5.14.4.1.


      Give a man a fish:  <%-{-{-{-<

        That's definitely a strange behavior, here's a fairly short way to reproduce it:

        $ perl -wMstrict -MDevel::Peek -le 'my $x="11a"; $SIG{__WARN__} = sub{$x="99"}; print "num: ",$x+0,", str: $x"; Dump($x)' num: 11, str: 99 SV = PVNV(0x59efe266a440) at 0x59efe2692648 REFCNT = 2 FLAGS = (POK,IsCOW,pIOK,pNOK,pPOK) IV = 11 NV = 11 PV = 0x59efe26d3ae0 "99"\0 CUR = 2 LEN = 10 COW_REFCNT = 1

        My guess is it's the signal handler: I am guessing it gets called during the $n+0 operation which produces the warning, and so an assignment to the string component of the variable during that operation leaves it in a state where the string and numeric components differ. So it's very similar to:

        $ perl -wMstrict -MDevel::Peek -MScalar::Util=dualvar -e 'my $x=dualvar(11,"99"); Dump($x)'

        I'm not sure if this is worth a bug report or not.

        but I suspect it has something to do with the inherent dualvar nature of scalars (see Scalar::Util::dualvar): a scalar caches a string or numeric representation of its value if it was ever used as a string or number, and both representations may be present in the scalar at the same time and may represent entirely unrelated values!

        Wow, that is clever! So, it's not really a bug...!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11109080]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (6)
As of 2024-04-18 13:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found