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

Hi,

could someone please explain this behaviour of the following snippet to me?

for my $i (1..20) { print "$i: "; print (1-1/2000)**$i; print " - "; print (1-1/2000)**int($i); print " - "; print "" . (1-1/2000)**$i; print "\n"; }
This prints on my 5.18.1 on Debian:
1: 0.9995 - 0.9995 - 0.9995 2: 0.9995 - 0.9995 - 0.99900025 3: 0.9995 - 0.9995 - 0.998500749875 4: 0.9995 - 0.9995 - 0.998001499500063 5: 0.9995 - 0.9995 - 0.997502498750313 6: 0.9995 - 0.9995 - 0.997003747500938 7: 0.9995 - 0.9995 - 0.996505245627187 8: 0.9995 - 0.9995 - 0.996006993004374 9: 0.9995 - 0.9995 - 0.995508989507872 10: 0.9995 - 0.9995 - 0.995011235013118 11: 0.9995 - 0.9995 - 0.994513729395611 12: 0.9995 - 0.9995 - 0.994016472530913 13: 0.9995 - 0.9995 - 0.993519464294648 14: 0.9995 - 0.9995 - 0.993022704562501 15: 0.9995 - 0.9995 - 0.99252619321022 16: 0.9995 - 0.9995 - 0.992029930113615 17: 0.9995 - 0.9995 - 0.991533915148558 18: 0.9995 - 0.9995 - 0.991038148190984 19: 0.9995 - 0.9995 - 0.990542629116888 20: 0.9995 - 0.9995 - 0.99004735780233
So for some reason I get the correct numerical value of the expression (1-1/2000)**$i only when I force stringification...

Why is that?

Replies are listed 'Best First'.
Re: strange arithmetic
by BrowserUk (Patriarch) on Jan 09, 2015 at 19:57 UTC

    If you had warnings enabled you'd be seeing:

    C:\test>perl -wE"print (1-1/2000)**$i;" print (...) interpreted as function at -e line 1. Useless use of exponentiation (**) in void context at -e line 1.

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked
Re: strange arithmetic
by kennethk (Abbot) on Jan 09, 2015 at 20:42 UTC
    I don't think either explanation above is terribly clear, so I'll expound a little. You are getting bitten by order of operations. As described in Terms and List Operators (Leftward),
    print ($foo & 255) + 1, "\n";
    probably doesn't do what you expect at first glance. The parentheses enclose the argument list for print which is evaluated (printing the result of $foo & 255 ). Then one is added to the return value of print (usually 1). The result is something like this:
    1 + 1, "\n"; # Obviously not what you meant.
    To do what you meant properly, you must write:
    print(($foo & 255) + 1, "\n");
    Your code
    print (1-1/2000)**$i;
    is functionally equivalent to
    (print (1-1/2000))**$i;
    and not
    print ((1-1/2000)**$i);
    as you might think. The parentheses bind tighter to the print than to the exponentiation operator.

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      Thanks for the clarification, but the above postings were indeed clear enough.

      Had I simply used warnings (as suggested) I would not have come here - oh well...

      With regard to "print" I always thought that if you use parenthesis around the arguments you should not have blanks between "print" and the opening parenthesis.
      So in my ideal world Perl would distinguish between print($whatever) and print ($whatever), treating the parenthesis in the first case as part of the print-call while in the second case as part of the expression to be printed.

      But that of course is only my taste and might open another can of worms...

        In Perl, functions, including built in functions like print, act like named list operators. So Perl will look for a list of terms and expressions following the name of the function. Because, Perl, ( and ) are used as list delimiters (much like quotes delimit strings), if the first thing Perl sees after the function name is (, Perl interprets that as the start of a list. The matching ) will end that list. With out the parentheses, Perl has to look for other syntactic clues to determine the end of the list. The main reason for using parentheses around the arguments to a function is to disambiguate the end of the argument list.

        Making a special case for the presence of space between the function name and a ( would re-introduce the ambiguity and break a lot of existing code. Actually, it would be worse because if that ( were the beginning of a list, the matching ), while ending that list, would not be the end of arguments, which would be more confusing.

        Bottom line, far better to use warnings; (and use strict;).

Re: strange arithmetic
by nlwhittle (Beadle) on Jan 09, 2015 at 19:30 UTC

    I haven't found a direct answer for this (yet), (see Update below) but it's interesting that if you assign the math to another variable first, then print that variable, the number is printed correctly; for example:

    $i = 2; $j = (1-1/2000)**$i; print $j;

    The output is:

    0.99900025

    It seems that the print function is interpreting the number differently due to the parens. If you input a similar math problem directly into print, but without parens, the number comes out right.

    $i = 2; print 0.9995**$i;

    Output:

    0.99900025

    Update: print is only printing what is in the parens because it is treating that part like the complete input to a function, ignoring the exponent part. Adding the blank string in front changes print to list operator mode, printing the entire answer.

    To summarize: the two modes of print are:

    # function mode print (your code here);

    And

    # list mode print arg1, arg2, etc; # and/or the . operator
    --Nick
      ... print is only printing what is in the parens because it is treating that part like the complete input to a function ...

      Deparsing with full parenthesization (see O, B::Deparse) shows this clearly. (Note, in addition to "print (...) interpreted as function...", the warning about the return value of print being raised to an exponent and then thrown away!)

      c:\@Work\Perl\monks>perl -wMstrict -MO=Deparse,-p -le "my $i = 5; print (1-1/2000)**$i; print '' . (1-1/2000)**$i; " print (...) interpreted as function at -e line 1. Useless use of exponentiation (**) in void context at -e line 1. BEGIN { $^W = 1; } BEGIN { $/ = "\n"; $\ = "\n"; } use strict 'refs'; (my $i = 5); (print(0.9995) ** $i); print(('' . (0.9995 ** $i))); -e syntax OK c:\@Work\Perl\monks>perl -wMstrict -le "my $i = 5; print (1-1/2000)**$i; print '' . (1-1/2000)**$i; " print (...) interpreted as function at -e line 1. Useless use of exponentiation (**) in void context at -e line 1. 0.9995 0.997502498750313


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

Re: strange arithmetic
by hippo (Archbishop) on Jan 10, 2015 at 12:53 UTC

    The documentation for print covers this pretty explicitly when it states:

    Be careful not to follow the print keyword with a left parenthesis unless you want the corresponding right parenthesis to terminate the arguments to the print;

    It's the sort of thing that will only trap you once and the moral of the story is to use warnings as recommended by BrowserUK above and indeed the Basic Debugging Checklist.

Re: strange arithmetic
by shmem (Chancellor) on Jan 10, 2015 at 19:32 UTC

    To make print treat an expression in parenthesis as an argument (and not as the argument list) just put a '+' in front of the parenthesis' opening:

    for my $i (1..20) { print "$i: "; print+(1-1/2000)**$i; print " - "; print+(1-1/2000)**int($i); print " - "; print "" . (1-1/2000)**$i; print "\n"; }
    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'