Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Non-integer print output???

by jwkrahn (Abbot)
on Sep 10, 2021 at 00:03 UTC ( [id://11136622]=perlquestion: print w/replies, xml ) Need Help??

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

Just futzing around and I tried this:

$ perl -le'print ~0' 18446744073709551615 $ perl -Mbigint -le'print +( 2 ** 64 ) - 1' 18446744073709551615 $ perl -le'print +( 2 ** 64 ) - 1' 1.84467440737096e+19

And also:

$ perl -le'print 2 ** 49' 562949953421312 $ perl -le'print 2 ** 50' 1.12589990684262e+15

So why does perl convert a number that will obviously fit inside a 64 bit integer to floating point?

(Yes I read perlnumber.)

Replies are listed 'Best First'.
Re: Non-integer print output???
by syphilis (Archbishop) on Sep 10, 2021 at 00:53 UTC
    So why does perl convert a number that will obviously fit inside a 64 bit integer to floating point?

    Firstly, note that the ** operator always returns an NV:
    C:\>perl -MDevel::Peek -le "$x = 2 ** 3; Dump $x;" SV = NV(0x47cd88) at 0x47cda0 REFCNT = 1 FLAGS = (NOK,pNOK) NV = 8 C:\>perl -MDevel::Peek -le "$x = 2 ** 49; Dump $x;" SV = NV(0x33d148) at 0x33d160 REFCNT = 1 FLAGS = (NOK,pNOK) NV = 562949953421312 C:\>perl -MDevel::Peek -le "$x = 2 ** 50; Dump $x;" SV = NV(0x47cd38) at 0x47cd50 REFCNT = 1 FLAGS = (NOK,pNOK) NV = 1.12589990684262e+15
    (Maybe the question you should ask is "Why does the ** operator always return an NV ?". I don't know.)

    What you've displayed is that perl sometimes decides that an NV will be printed (interpolated) as an integer, and sometimes printed as a float.
    Obviously, non-integer NVs will always be printed as floats - no problems there.

    Now, perl still adheres to the utterly fuckbrained notion that, when nvtype is double, print() will never present more than 15 significant digits.
    And that's what you're seeing here.
    The NV returned by the operation 2 ** 50 contains 16 significant digits which, by this idiotic rule, must be interpolated to 15 digits. And that's what perl duly does.
    But if the NV contains an integer value comprising less than 16 significant digits that is in the range IV_MIN to UV_MAX (eg 2 ** 50 2 ** 49), then it will be interpolated as an integer.

    On a perl whose nvtype is long double fully quad IEEE 754 long double or __float128, you should find this particular anomaly to be absent - because the 15-digit limit does not apply there.
    Thre's still an absurdly low limit, but it's large enough to prevent this particular case presented here.

    Cheers,
    Rob
      > (Maybe the question you should ask is "Why does the ** operator always return an NV ?". I don't know.)

      The exponentiation operator a**b is IIRC optimized to generally handle float input for a and b and is approximating the result.

      The result is hence usually a float.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        The exponentiation operator a**b is IIRC optimized to generally handle float input for a and b and is approximating the result.

        Does this arise because C's pow/powl/powq functions take floating point inputs and return floating point values ?
        I've always assumed so, without ever really knowing.

        I'm a little surprised that the C language doesn't provide a pow() function that takes and returns signed long int (or signed long long int) values - though I guess such a function would easily overflow for relatively low input values.

        Cheers,
        Rob

      Thanks for the info.

Re: Non-integer print output???
by ikegami (Patriarch) on Sep 11, 2021 at 14:54 UTC

    Why not? Exponentiation is an operation done using floating point numbers. And there's no point in checking if the number is an integer within range of the user's IV or UV types, and converting it to those from the NV.

      Exponentiation is an operation done using floating point numbers. And there's no point in checking if the number is an integer within range of the user's IV or UV types, and converting it to those from the NV.

      I don't think it's quite that simple.
      Consider the example of 2** 50 provided by the OP - where the result was given as 1.12589990684262e+15.
      That particular result is absolute rubbish. The correct figure is 1125899906842624.0 (which is a different value, yet one that is exactly representable inside that perl's NV) :
      C:\>perl -le "print 'wtf' if 2 ** 50 == 1.12589990684262e+15;" C:\>perl -le "print 'ok' if 2 ** 50 == 1125899906842624.0;" ok
      Why are we so ready to settle for a half-arsed floating point approximation when an exact floating point (or integer) representation is available ?

      Also consider that, on a perl whose ivsize is 8 && whose nvsize is 8, you will be told that 3 ** 40 (which equates to less than ~0) is 1.21576654590569e+19.
      This is a completely different value to the exact 12157665459056928801, which would be presented if perl DWIMmed by performing integer exponentiation.
      And perl prides itself on DWIMming .... ??

      Cheers,
      Rob

        That particular result is absolute rubbish.

        Not at all. In fact, your snippet proves that the correct result is returned. You think the incorrect result is returned because you incorrectly assume print doesn't perform any rounding.

        Why are we so ready to settle for a half-arsed floating point approximation when an exact floating point (or integer) representation is available ?

        It's not an approximation. The result is 100% exact in this case. (Powers of two literally require only 1 bit of precision, and that bit isn't even stored.) There are cases where exponentiation could produce more accurate numbers using ints than floats on a build with 64 bit ints. But this is eclipsed by the number of cases that are more precise using floats (e.g. 2**0.5), and it's also eclipsed by the number of cases that are only possible using floats (e.g. 2**65).


        It looks like you are raising your own question about why scientific notation is used by print or why it rounds, but the question asked was about why exponentiation returns "a float instead of a 64-bit number". That has nothing to do with how print stringifies floats. Sure, that could be improved, but that's a different question.

        Upd: Rewritten

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11136622]
Approved by kcott
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (2)
As of 2024-04-20 01:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found