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

Is there a feature, function, or situation in Perl where a variable is forced to be an integer and, from then on, all calculations done with that variable will yield an integer result? I'm finding that any calculations I do with one variable always yield integer results.

I'm doing some calculations for interest on a credit line. (I know that sounds like a homework assignment, but it's for investing. I have three different groups that are using the same credit line and I need to keep track of the interest for each one for what comes out of the credit line.)

The problem is, at one point, I call a routine and get a number for a result. It's stored in $gap. Then any calculations I use $gap in will, from then on, always give me an integer as a result instead of a decimal.

I got tired of this issue and tried adding this line:

$gap = "$gap.00";
Once I put that in, all calculations from then on worked fine. No more integer results or rounding.

There's a lot of code that is involved (several subroutines) in producing the result for $gap. But all the functions are either substr($gap, $start, $len) or straight and simple addition or subtraction, except I do, at one point, use Time::Local::localtime() to get the ticks time value from a string formatted like yyyy-ddmm.

What can change a variable so from that point on any math done with that variable yields an integer answer? And what does the one line above do to force that to stop?

I made a test program and module to go with it. While doing this, and checking the original modules, I found this in one:
use Math::BigInt; use Math::BigInt ':constant';

This seems to be the problem. I don't remember all the details, but I needed it in the module, so I can't just comment it out. I tested it by copying code from the module into the main test program and seeing what would and would not work with those two statements commented out. If they were not there, then I would get non-integer results.

I created the code below, a program (I called it testintegers) and a module (I called it IntTest). Note one line that's commented out - marked with a comment before it containing the text "Removable code #1" to make it easy to find. In this program, note the line:

$mins = tdiff($now, $then);

That's where I call the routine in the module and after that, $mins is not only an integer, but any calculations that use $mins will provide only integer results. If I uncomment the next line of code (the removeable code #1), then after that, I can use $mins and get non-integer results.

What does BigInt do to the variable that forces any future calculations with it yield non-integer answers? Is there some way I can make a change, before returning the value from tdiff (in the module that uses BigInt) to stop this issue? And if not, is there something I can do with the returned value, other than add a ".00" to the end to force it to be treated as a non-integer?

Here's the code from my test program, testintegers:

#!/usr/bin/perl use strict; use IntTest; use Time::Local; my ($mins, $days, $now, $then, $elapsed); ($now, $then) = @ARGV; if (!$now) {$now = "2013-0923";} if (!$then) {$then = "2013-1023";} $mins = tdiff($now, $then); #Removable code #1 # $mins = "$mins.00"; #Gap is the difference in minutes, so change it to days: $days = ($mins / 60) / 24; #Divide by days in a year so it can be used in interest formula $elapsed = $days/365; print "Now: $now, Then: $then, Minutes: $mins, Days: $days, Elapse +d: $elapsed\n";

And here's the module I also created for testing, IntTest:

# package IntTest; #These two lines are the culprit. Without them, it's fine use Math::BigInt; use Math::BigInt ':constant'; use Time::Local; use Exporter; use strict; BEGIN { use vars qw(@ISA); @ISA = qw(Exporter); our @EXPORT = qw(tdiff tconvert); } sub tdiff { my ($now, $then, $diff); ($now, $then) = @_; if (!$then) { $then = $now; $now = tconvert(); } $now = tconvert($now); $then = tconvert($then); $diff = int(($then - $now) / 60); return $diff; } #Take time and dates in the format yyyy-mmdd or yyyy-mmdd-hhmmss #and convert it to ticks sub tconvert { my $start = shift(@_); $start =~ s/-//g; my $year = substr($start, 0, 4); my $month = substr($start, 4, 2); my $day = substr($start, 6, 2); my $hour = substr($start, 8, 2); my $min = substr($start, 10, 2); my $sec = substr($start, 12, 2); $year = $year - 1900; $month--; my $now = timelocal($sec, $min, $hour, $day, $month, $year); return $now; } 1;

I ran these with this command line:

perl -I.. ./testintegers 2013-0923-173422 2013-0923-182819

Note that when you run it, the results printed out are integer, but when the removable line of code is uncommented, the results are non-integer. If this hack will work, I'm okay using it, but from comments here, it seems like it's not a reliable method, so I'd like to find a way to eliminate the problem of getting only integer answers from any math done with the value returned from tdiff. </p?

Replies are listed 'Best First'.
Re: All Calculations Done with One Variable Give Integer Answers
by BrowserUk (Patriarch) on Sep 23, 2013 at 17:08 UTC

    See the integer pragma. All math within its scope are done using integer operations.


    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.

      I'm not using integer.

      If a subroutine in someone else's module uses it, or if they use int(), would convert it to an integer and force all math with it from then on to yield an integer result? If so, how can I undo that?

        If a subroutine in someone else's module uses it, or if they use int(), would convert it to an integer and force all math with it from then on to yield an integer result?

        No, to either or both.

        However, unless you do division (or subtract a fractional value) results will be integers. But you probably realise that.

        Unless the variable in question is being tied some how, this sounds more likely to be a problem with how you are displaying the value rather than a scalar magically becoming strictly integer.


        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.
Re: All Calculations Done with One Variable Give Integer Answers
by MidLifeXis (Monsignor) on Sep 23, 2013 at 17:48 UTC

    This isappears to be crossposted at stackoverflow (well, sort of). It is considered neighborly to indicate when a question is cross posted.

    --MidLifeXis

      I apologize. My bad. I've never done that and after I posted on SO, I was thinking, "Hmm... Sounds more like a question for Perl Monks," and, after no sleep last night, I'm a bit sluggish.
Re: All Calculations Done with One Variable Give Integer Answers
by syphilis (Archbishop) on Sep 23, 2013 at 23:38 UTC
    Hi,

    Could you, at some point in your code where $gap is producing this "integer" behaviour, insert the following piece of code:
    use Devel::Peek; Dump($gap);
    and then provide us with the output of that Dump().

    $gap = "$gap.00";


    I suspect that you'll get the same change in behaviour by doing simply:
    $gap = "$gap";
    Cheers,
    Rob
      I'm not clear whether I should try dumping after I get $gap (now $mins in the code I've added) from the module or before that. But you are right. Just using quotations is enough to make it change behavior. With that known, can you tell me just what is happening and if that's a reliable hack?

        The line $mins = "$mins.00"; turns the Math::BigInt object into a standard Perl scalar. If you inspect $mins using Data::Dumper before and after $mins = "$mins.00"; you get:

        $VAR1 = bless( { 'value' => [ 43200 ], 'sign' => '+' }, 'Math::BigInt' ); $VAR1 = '43200.00';

        So (with hindsight!) had you used Data::Dumper as a first step to see what's going on, you would have been told immediately. But this is only with hindsight...

Re: All Calculations Done with One Variable Give Integer Answers
by ww (Archbishop) on Sep 23, 2013 at 17:36 UTC

    What is $gap = "$gap.00"; supposed to do? What do you think it does? Are you using the dot to try to concat to establish a two digit decimal field (syntax is wrong) or what?

    I ask because I don't see the results you seem to describe in either of these cases:

    C:\>perl -E "my $gap = 1.23; $gap = $gap.00; print ($gap + 1.5);" 2.73 # not an integer C:\>perl -E "my $gap = 1; $gap = $gap.00; print ($gap + 1.5);" 11.5 # not even close! And not just BTW, WTF happens h +ere?

    I think we need a very small version of your remaining code (say 20 lines or so starting with an assignment to $gap of a typical non-date, numeric value) to know what's going on.

    If I've misconstrued your question or the logic needed to answer it, I offer my apologies to all those electrons which were inconvenienced by the creation of this post.
      If $gap was previously equal to, say, 55, and I use $gap = "$gap.00" and print the result, I get 55.00. I guess I could use $gap = $gap."00" or other versions, but I tried this version and once I did that, from then on, any math done with $gap was no longer yielding integer results.
        That doesn't seem to be what happens here   (perl -v: 'This is perl 5, version 16, subversion 2 (v5.16.2) built for MSWin32-x86-multi-thread (with 1 registered patch....)'   whether the value of $gap is numeric or stringified:
        C:\>perl -E "my $gap = 55; $gap = "$gap.00"; say $gap;" 550 C:\>perl -E "my $gap =\"55\"; $gap = "$gap.00"; say $gap;" 550

        Strongly suggest you explain or correct your latest statement.

        In light of marinersk's thoughtful check, remove emphasis, modify language to say:
        Suggest you tell us your version of Perl (and show compact version of actual code, as suggested above).
Re: All Calculations Done with One Variable Give Integer Answers
by boftx (Deacon) on Sep 23, 2013 at 23:53 UTC

    Not sure if this applies, but have you looked at Math::Currency on CPAN?

    On time, cheap, compliant with final specs. Pick two.
Re: All Calculations Done with One Variable Give Integer Answers
by BillKSmith (Monsignor) on Sep 24, 2013 at 03:59 UTC
    There probably is no 'easy' solution. The problem is explained in perlfaq4. Read
    perldoc -q "Why am I getting long decimals" perldoc -q "Does perl have a round"
    Bill