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

I'm having an issue with numeric overloading using Perl compiled with 64-bit integers. When the numerification of an object returns an integer > 10^15, Perl is coercing it to a floating-point number despite the fact that 64-bit integers can go over 10^19.

The following code illustrates:

#!/usr/bin/perl use strict; use warnings; package aaa; { use overload ( '""' => sub { return(1000000000000000) }, '0+' => sub { return(1000000000000000) }, 'fallback' => 1 ); sub new { return (bless({}, shift)); } } package bbb; { use overload ( '""' => sub { return(999999999999999) }, '0+' => sub { return(999999999999999) }, 'fallback' => 1 ); sub new { return (bless({}, shift)); } } package main; { print("Object overloaded as 16-digit integer\n"); my $a = aaa->new(); print("STRING: $a\n"); print('NUMBER: ', 0 + $a, "\n\n"); print("Object overloaded as 15-digit integer\n"); my $b = bbb->new(); print("STRING: $b\n"); print('NUMBER: ', 0 + $b, "\n\n"); print("Addition with 16-digit integer\n"); print('NUMBER: ', 0 + 1000000000000000, "\n"); }
It produces:
Object overloaded as 16-digit integer
STRING: 1000000000000000
NUMBER: 1e+15

Object overloaded as 15-digit integer
STRING: 999999999999999
NUMBER: 999999999999999

Addition with 16-digit integer
NUMBER: 1000000000000000
It's the '1e+15' above that I object to.

Update:Using int(0 + $a) does restore the result back to an integer, but that doesn't address my desire to negate the floating-point conversion in the first place.

Is there a way to modify this behavior? If not, I would think this should be reported as a bug, no?

Remember: There's always one more bug.

Replies are listed 'Best First'.
Re: Numeric overloading with 64-bit Perl
by Moron (Curate) on Oct 06, 2005 at 08:12 UTC
    As I understand it, this is what the use integer; pragma is for... Test results: Putting this at the top of your code under the other pragma declarations did indeed make the problem go away when I tested it.

    -M

    Free your mind

      Yes, use integer will coerce the overload result back to integer format, as will using the int() function. But these are just workarounds. (And use integer is worse because it affects all arithmetic within a block, or the entire application, if placed at the top as you recommended.)

      The issue is how to stop the needless coercion of the integer return value (1000000000000000) to a floating-point result (1e+15) in the first place.


      Remember: There's always one more bug.
        The manual, clearly describes that "use integer;" controls the actual computation, rather than coercing anything afterwards. Specifically, when use integer is in effect, floating point numbers will be rounded, but integers will not be coerced into floating point - they will stay integer before during and after any computation in the relevant scope.

        I suggested placing it at the top of the example program because the example program had only one purpose - to show where float conversion took place for 16 digits. This should not be construed as a recommendation to always place it at the top of every program.

        Re your subsequent objection to the block scope nature of use integer, I can't conceive of any actual case where the use of

        { use integer;" # statements where the pragma is required # ... "}"
        wherever the pragma is actually required, can fail to control the scope - curly brackets can be applied anywhere you like!

        -M

        Free your mind

Re: Numeric overloading with 64-bit Perl
by tonyc (Hermit) on Oct 06, 2005 at 12:11 UTC

    I suspect the problem you're running into is the way perl converts numbers to strings, not the way it's storing them.

    By default perl will format numbers as if sprintf()ed with a format of "%.Ng" where N is the value of DBL_DIG for your platform. To check this value run:

    perl -MPOSIX -le "print DBL_DIG"

    On my system this is 15, if it's the same on your platform, this could explain why you're seeing the change when moving from 15 to 16 digits.

    To see how perl is actually storing the number use Devel:Peek:

    perl -MDevel::Peek -le '$x = 1000000000000000; Dump($x)'

    For a use64bitint perl this produced:

    SV = IV(0x80cba4) at 0x800d1c REFCNT = 1 FLAGS = (IOK,pIOK) IV = 1000000000000000

    If you want numbers formatted to higher precision on output, you'll need to either change $#, or use printf with an appropriate format.

      No, it is not the result of number-to-string conversion. Consider the following:
      my $x = 100000000000000; for my $ii (14..25) { print("10^$ii: ", 0 + $x, "\n"); $x *= 10; }
      which outputs:
      10^14: 100000000000000
      10^15: 1000000000000000
      10^16: 10000000000000000
      10^17: 100000000000000000
      10^18: 1000000000000000000
      10^19: 10000000000000000000
      10^20: 1e+20
      10^21: 1e+21
      10^22: 1e+22
      10^23: 1e+23
      10^24: 1e+24
      10^25: 1e+25
      
      This shows that Perl can output 20-digit integers, and verifies that the integer return value is being coerced into a floating-point result by the overload handling code.

      Remember: There's always one more bug.

        You're right. Sorry for the assumption.

        I think I see the cause of the problem. If you look at pp_add (pp_hot.c) you'll see that it checks for the IV and UV flags on the scalar (SvIOK, SvUOK) and since neither of these is set it falls through to do the arithmetic with NVs.

        To fix this the magic code would need to be hoisted out of sv_2iv_flags() and the check for SvIOK (or SvUOK) done on the scalar returned by the numeric overload.

        Fixing this for every operator would be a big change.

        Edit: pp_add is in pp_hot.c, not sv.c