Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Integer detection concept

by boo_radley (Parson)
on May 15, 2001 at 00:13 UTC ( [id://80328]=perlquestion: print w/replies, xml ) Need Help??

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

Posted below is a simple routing to determine if a number is integer. This method seems pretty straightforward, but the question of how to determine a number's 'integeriness' pops up from time to time, which leads me to believe that I'm Missing Something.
Can anyone suggest why this code would be frowned upon?
foreach (5.6, 2, 5.000000001, 3, -2, -3, -4.12){ print &is_int ($_)?"$_ is an integer\n":"$_ is not an integer\n"; } sub is_int{ my $testvar =shift; use integer; my $intval=$testvar_*1; no integer; return $testvar == $intval; }

2001-05-14 Edit by Corion : Moved from PMD to Seekers

Replies are listed 'Best First'.
Re: Integer detection concept
by merlyn (Sage) on May 15, 2001 at 00:21 UTC
      For regular numbers, this is works, but it also claims 'integerness' for things like '2na' and '4ever'. Would a regexp be a reliable way of catching this, or is there something better?
      sub is_int { ($_[0] =~ /^\-?\d+(?:\.\d+)?(?:e\+\d+)?$/ && $_[0] == int $_[0]); }
      I realized this after I pushed 'submit'... I'm a dolt.
(tye)Re: Integer detection concept
by tye (Sage) on May 15, 2001 at 00:40 UTC

    Well, I can think of quite a few meanings of "is an integer".

    1. Fits in a Perl UV:
      sub isUV { my( $i )= @_; return isIntVal($i) && $i == (0|$i); }
    2. Fits in a Perl IV (no test provided)
    3. Fits in a Perl NV but has no fractional part:
      sub isNVint { my( $i )= @_; return isIntVal($i) && $i == int($i); }
    4. Is the (string) representation of an integer that may not fit into (1), (2), or (3):
      sub isInt { my( $i )= @_; return $i =~ /^-?\d+\z/; }
    5. A scalar that will result in (1), (2), or (3) w/o warning when used in a "numeric context":
      sub isIntVal { my( $i )= @_; return $i =~ /^\s*[-+]?\d+\s*$/; }
      But (update) that doesn't cover "1.2e4", for example, so you can also use (updated):
      sub isIntVal2 { my( $i )= @_; my $warn= 0; { local( $^W )= 1; # my $warn= 0; local( $SIG{__WARN__} )= sub { $warn++ }; $i= 0+$i; } return ! $warn && int($i) == $i; }
    6. A string that will result in literal (1), (2), or (3) w/o warning when used in Perl code (for example, with eval). This one has to allow for _ in the middle, "0x" or "0b" at the start, etc.
    And, in particular, (4) has plenty of room for changes based on what you think should be allowed. And my test for (3) doesn't account for numbers that don't accurately fit in an NV.

    I really think that the best solution involves making the looks_like_number() C subroutine that Perl itself uses available to scripts.

            - tye (but my friends call me "Tye")

      No. 5 looks like a pretty cool solution to the problem. There's one thing with this code though that I can't figure out. It looks to me that you're using a closure, but it doesn't work. When I try to run this code Perl complains: Global symbol "$warn" requires explicit package name

      I could only make this work by removing the outer { }. Does anyone know why this closure doesn't work?

        Sorry, my bad. I fixed the code and left the original broken bit in, just commented out.

                - tye
        That's because it's not a closure. Closures are subroutines that maintain the lexical state around them outside the scope of that lexical state. There's no subroutine involved.

        The reason for the extra brackets is to provide a scope for the local $^W = 1;.

        Being right, does not endow the right to be rude; politeness costs nothing.
        Being unknowing, is not the same as being stupid.
        Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
        Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: Integer detection concept
by traveler (Parson) on May 15, 2001 at 01:45 UTC
    I am confused by the question. Consider
    $x = 17; for $i (1..10){ $x = sqrt($x); } for $i (1..10){ $x *= $x; } print $x;
    prints 17.00000000000002 on my perl 5.005_003 on an x86. Is that an integer? merlin's comment shows how to see if a number is a "good" integer, but if you are using this in some mathematics, you might check for some fudge as it is fairly easy to get a non-integer that "should be" an integer.

    You probably knew that, but it might not be obvious to everyone...

    --traveler

Re (tilly) 1: Integer detection concept
by tilly (Archbishop) on May 15, 2001 at 03:40 UTC
      I'm not computer whizzy enough to explain in detail but I think that if you ask a computer to store a floating point number which is obtained as a fraction (eg. $a = 1/3;) then you get 0.333333333333333 (not 0.3 recurring) to however many decimal places. If yuo then operate on this number, because it does not remain a fraction, the results are "rounded". So to compare the result of iakobski's sqrt and subsequent squaring to 1/3 would not match using ==.

      In this case it is only integers (whole numbers - no decimal places) which are being discussed so they must be stored more sensible and a numerical comparison (==) will be more efficient than comparing the two strings (especially on large numbers, possibly several hundreds of thousands of times through a loop).

      I think essentially the eq and ne have to go through both "strings" a character at a time and compare them but == or != can just say these numbers are('nt) the same.

      larryk

      ---------------------------------------------------- $less->{'chars'} = `"time in the pub" | more`; # :-D
Re: Integer detection concept
by mothra (Hermit) on May 15, 2001 at 17:33 UTC
    As one might expect, this is already answered in the FAQ:
    How do I determine whether a scalar is a number/whole/integer/float?

    Assuming that you don't care about IEEE notations like "NaN" or "Infinity", you probably just want to use a regular expression.

    ... if (/^-?\d+$/) { print "is an integer\n" } if (/^[+-]?\d+$/) { print "is a +/- integer\n" } ...
Re: Integer detection concept
by tachyon (Chancellor) on May 16, 2001 at 03:46 UTC
    Your code and Randal's code only works if you believe a foobar is an integer ;-)
    C:\>type test.pl foreach (1.2, foobar){ print &is_int ($_)?"$_ is an integer\n":"$_ is not an integer\n"; } sub is_int{ my $testvar =shift; use integer; my $intval=$testvar_*1; no integer; return $testvar == $intval; } C:\>perl test.pl 1.2 is not an integer foobar is an integer C:\> C:\>type test.pl $_ = 'foobar'; print "'$_' is an integer" if is_int($_); sub is_int { $_[0] == int $_[0]; } C:\>perl test.pl 'foobar' is an integer C:\>
    Sadly this is not the only problem. You might also try testing foo2you and foo2.1
     
    tachyon

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (6)
As of 2024-04-19 03:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found