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

Hi,
Hmmm ... perhaps that should be "Exasperated by" ... any grammarians out there ?

Anyway, it might not look much but the following (which applies only to 64-bit builds of perl) makes me quite angry:
use warnings; use integer; # makes no difference # 144115188075868217 == (2 ** 57) + 12345 $string1 = '144115188075868217'; $string2 = '144115188075868217crap'; $string1 += 0; { no warnings 'numeric'; $string2 += 0; } print $string1, "\n"; #prints 144115188075868217 ... correct print $string2, "\n"; # prints 144115188075868224 ... wrong
The first question:
Can someone tell me where it is documented that the strings '144115188075868217' and '144115188075868217crap' will be evaluated differently (in numeric context) on a 64-bit integer build of perl ?

The following also angers me:
use warnings; use integer; # makes no difference $num = 1.44115188075868217e17; print $num, "\n"; #prints 1.44115188075868e+017
The second question:
Is there some way (on a 64-bit integer build of perl) I can get scientific notation of 58-bit integers to DWIM ?

(Some other absurdity is in progress here, too. Although I wanted to see either 144115188075868217 or 1.44115188075868217e17 as output, I actually expected to see 1.44115188075868224e17 ... couldn't even get that much precision.)

With 32-bit builds of perl there's no problems with representing integers as doubles (since precision is never lost). But with 64-bit integer builds of perl you lose integer precision when you switch to the "double" representation ... which makes me wonder why switching to "double" representation on 64-bit integer builds of perl is given such a high priority.

We have a saying at $work regarding the wisdom of the powers that be (and which sums up perfectly our understanding of their actions):
"They know what they're doing."

For a glimpse of the real world problem:
Let's suppose there's a C library - and a perl extension that accesses that C library. The C library can return a string to perl (via XS) such as "1.44115188075868217e17". I would like to be able to convert that string to the numeric value 144115188075868217 on my 64-bit integer build of perl. I would expect that $num = "1.44115188075868217e17"; would do the trick. There's no problem with that approach regarding 32-bit integers (expressed in scientific notation) on 32-bit builds of perl but, as demonstrated above, it won't work with 64-bit integer builds. Instead I have to remove the "." and remove the "e17" (or "E17", or "e+017", etc., as the case may be). My first thought was to
$string =~ s/\.//; $string =~ s/e/crap//i;
But no ... I can't just replace the 'e'/'E' with 'crap' ... I have to remove the 'e' plus whatever follows it. (For some anal-retentive-nitpicker-type-inexplicable reason that really pisses me off.)

Ok .... so it's no big deal ... but this is perl ... where things are supposed to "just work". (And I'm hard pressed to understand why, in this particular instance, things have been designed to "just not work".)

Cheers,
Rob

Replies are listed 'Best First'.
Re: Exasperated with 64-bit integer builds of perl
by ysth (Canon) on May 28, 2007 at 16:40 UTC
    You'll get more salutary results if you build a perl that uses long doubles too.

    The whole preserve-results-as-an-integer is something of a bandaid - there are places it covers and places it doesn't.

    The "design" is that pretty much any conversion or operation result in a floating point value. Doing anything else is the result of trying to make things "just work" after the fact.

      build a perl that uses long doubles too

      Yes, it becomes very difficult to always pick the right choice when you build things such that some numbers only fit in IVs and some only fit in NVs. Perl has a long history of all IVs also fitting into NVs and trying to fight both that history and expecting some magical perfection in Perl opcodes being able to predict whether the results should be calculated as an IV or an NV is going to lead to disappointment.

      - tye        

Re: Exasperated with 64-bit integer builds of perl
by rhesa (Vicar) on May 28, 2007 at 14:25 UTC
    FWIW, using int($num) does produce an integer (albeit the wrong one):
    $num = 1.44115188075868217e17; print int($num); __END__ 144115188075868224
    I get the same number with print int( $string2 ).

    This is perl, v5.8.8 built for x86_64-linux-thread-multi

      Hi rhesa,
      As I understand it (and a Devel::Peek::Dump($num) would confirm it), $num is created as a double (53 bits of precision), which of course, can't accurately handle the 58-bit integer 1.44115188075868217e17.

      One of the things I'm wondering is why perl has been set up to assign integral values like 1.44115188075868217e17 to a "double" on 64-bit integer builds - even when 'use integer' is specified. I suspect it's some sort of 32-bit legacy, but I don't really know.

      Thanks for the feedback.

      Cheers,
      Rob
Re: Exasperated with 64-bit integer builds of perl
by Moron (Curate) on May 29, 2007 at 15:35 UTC
    Why "use integer" makes no difference, IMO, is that Perl recognises the example expression as floating point and will only apply the use integer pragma to fixed point notation - which suggests that for question 2 the answer is to convert the notation first, perhaps using Math::FixedPrecision along the way.
    __________________________________________________________________________________

    ^M Free your mind!

      Why "use integer" makes no difference, IMO, is that Perl recognises the example expression as floating point

      Yes, I think it's likely that perl does precisely that ... but it's rather simplistic of perl to assume that the value should be represented as an NV (double) just because scientific notation was used. (Of course, on a 32-bit build of perl it won't matter, but on a 'use64bitint' build of perl it's not a very bright thing to do, imo - unless 'uselongdouble' is also in place as ysth recommended - in which case it'll be ok).

      As tye suggested, I think there's an element of "history" here. (I referred to the same thing in one of my posts in this thread as "32-bit legacy".) No matter what you call it, I don't think it's a very convincing excuse.

      tye also referred to "perl opcodes" - and, I must confess, that I have no idea how that impacts upon perl's capacity to get it right.

      My hunch (and it's no more than that) is that there's a lack of interest in getting perl to do the right thing wrt 'use64bitint' builds ... and, furthermore, that's the main reason that perl doesn't do the right thing.

      I mean, how hard would it be for perl to determine that 1.44115188075868217e17 represents an integer (especially with use integer;), stick that value in an IV slot (given that it fits there), and turn on the IOK flag ? I don't know the answer to that question, btw ... but Shirley that wouldn't be all that difficult for the current perl pumpkings to implement. (As always, I stand to be corrected.)

      Anyway, perl's one redeeming feature is that it at least gives one the tools to fix the problems it creates :-)

      Cheers,
      Rob

        You've got one case that isn't done as well as you'd like. Somebody could spend some time doing some dodgy hack to try to take care of this one case (have you started writing this patch? -- I don't believe your assessment of the difficulty involved if you haven't). Then you'd be left to whine about a ton of other cases that you just haven't run into yet.

        So what's your brilliant, simple way to just know that 1.4...e17 (and every other numeric literal) fits in this perl's version of IV "better" than in this perl's version of NV? Are you going add comparisons/tests to determine this each time Perl runs into a literal? And then, for each of the new cases you eventually run into, add more comparisons/tests again, basically boiling down to doing every calculation twice and then doing extra work to determine whether the NV or IV result is "better"?

        Stop looking at just your one pet case and try to look at the larger problem. Predicting whether a result would be better represented in an IV or an NV isn't a simple problem, whether you'd like to think it is or not... except in the excellent case of making the NVs able to hold any IV, which is a route open to you.

        So your argument boils down to not really understanding how to do it, not being interested in doing it yourself, rather blithely assuming that it isn't difficult for someone else to do for you, complaining because they aren't running off to do it, and not offering any response as to why you aren't fixing the problem yourself by just changing one more option in your build. Can you see how that might not be persuasive to everyone? :)

        - tye        

        "use integer;" works on fixed point values INDEPENDENTLY of how many bits the machine has or how many bits are needed to represent a given value. Limiting it to fixed point assures that the functionality is injective. What you are asking for would downgrade it to non-injective, i.e. would make it work and not work according to a bits-needed comparison between a) the value being operated on and b) the size of the registers in the machine.

        I have meanwhile thought of another way to get what you want: sprintf. If the number is floating point, it internally uses the C sprintf, thereby achieving much high performance for your requirement than changing the meaning of use integer ever could anyway.

        __________________________________________________________________________________

        ^M Free your mind!