Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

numeric comparison

by hotshot (Prior)
on Jul 15, 2003 at 09:07 UTC ( [id://274317]=perlquestion: print w/replies, xml ) Need Help??

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

hello guys!

How can I check that a variable I have is numeric before comparison in order not to get the 'Argument "f" isn't numeric in numeric lt (<) at ..' warning, as in:
my $foo = 'xxx'; if ($foo < 4.556) { # will throw the warning # do something }
Trying to match to a regexp /^\d$ is not enough when dealing with fractions (because of th period).

Thanks

Hotshot

Replies are listed 'Best First'.
Re: numeric comparison
by gjb (Vicar) on Jul 15, 2003 at 09:12 UTC

    Have a look at Regexp::Common, it has patterns for real numbers (and many other common things).

    Just my 2 cents, -gjb-

Re: numeric comparison
by Abigail-II (Bishop) on Jul 15, 2003 at 09:24 UTC
    If it's ok if 'xxx' compares as 0, you could also turn the appropriate warning off (locally). Something like:
    if (do {no warnings 'numeric'; $foo < 4.556}) { ... }

    Abigail

Re: numeric comparison
by dbp (Pilgrim) on Jul 15, 2003 at 10:08 UTC

    This topic is covered nicely in the Perl Cookbook. Here's the applicable code:

    warn "has nondigits" if /\D/; warn "not a natural number" unless /^\d+$/; # rejects -3 warn "not an integer" unless /^-?\d+$/; # rejects +3 warn "not an integer" unless /^[+-]?\d+$/; warn "not a decimal number" unless /^-?\d+\.?\d*$/; # rejects .2 warn "not a decimal number" unless /^-?(?:\d+(?:\.\d*)?|\.\d+)$/; warn "not a C float" unless /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;
Re: numeric comparison
by BrowserUk (Patriarch) on Jul 15, 2003 at 10:34 UTC

    This is the function I used to use for this.

    sub is_numeric{ use POSIX (); !(POSIX::strtod($_[0]))[1]; }

    However, if you download and build the latest cpan version of Scalar::Util (the version that comes with 5.8.0 doesn't have it), it provides access to perl's internal looks_like_number(); which ought to be quicker than either strtod() or a regex.

    NOTE: If your not set up to build XS modules, then List::Util and Scalar::Util will silently fall back to using Perl implementations of the functions they provide. In the case of Looks_like_number(), this is a regex.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller

      Perl 5.8.1's stock Scalar::Util has looks_like_number(). Here's a little workout to show what it can and can't do,

      use Scalar::Util 'looks_like_number'; my @candidates = qw/ 1 1.1 1,1 0E0 0e0 0377 100_000 0xdeadbeef/; print $_, looks_like_number($_)? ' is' : ' is not', ' a number.', $/ for @candidates; __END__ 1 is a number. 1.1 is a number. 1,1 is not a number. 0E0 is a number. 0e0 is a number. 0377 is a number. 100_000 is not a number. 0xdeadbeef not is a number.

      After Compline,
      Zaxo

        I was a bit surprised by 100_000 not being recognised as a number but then I tried this

        print 3 + '0xdeadbeef'; Argument "0xdeadbeef" isn't numeric in addition (+) at ... 3 print 3 + 0xdeadbeef; 3735928562 print 3+ '100_000'; Argument "100_000" isn't numeric in addition (+) at ... 103 print 3+ 100_000; 100003

        Which I guess means that the code used to extract a number from a string at runtime is a different routine to that used for parsing numeric constants at compile time. Which is slightly disappointing, but is probably done that way for very good reasons.

        I just wish that the version of Scalar-List-Utils on the AS 5.8 list included the compiled XS code. That is that possible isn't it?


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller

Re: numeric comparison
by derby (Abbot) on Jul 15, 2003 at 12:12 UTC
    dbp gave you the faq supported answer and it can be found in perlfaq4 under "How do I determine whether a scalar is a number/whole/integer/float?". Then check the Floating-point Arithmetic section of perlop as to why your comparison of a float is going to lead you down a subtle path of bugs.

    -derby

Re: numeric comparison
by Tanalis (Curate) on Jul 15, 2003 at 09:12 UTC
    Why not use a regexp? Something like ...
    unless ($foo =~ /^\-?\d*\.?\d*$/) { # do something }
    should do the trick.

    Hope that helps.

    -- Foxcub
    #include www.liquidfusion.org.uk

    Update: Doh, updated regexp to something that stands a chance in hell of actually working .. (thanks, antirice)

      So "..." and "-124.-8.5-" are valid numbers?

      antirice    
      The first rule of Perl club is - use Perl
      The
      ith rule of Perl club is - follow rule i - 1 for i > 1

Re: numeric comparison
by antirice (Priest) on Jul 15, 2003 at 09:15 UTC

    I don't know if you want it to work with octal or hex but this will work for decimal.

    $word =~ /^-?(\d*)\.?(\d*)$/ && (length($1) || length($2));

    What's with the length checks? ".807" and "30." are valid numbers whereas "." is not. Hope this helps.

    antirice    
    The first rule of Perl club is - use Perl
    The
    ith rule of Perl club is - follow rule i - 1 for i > 1

Re: numeric comparison
by edan (Curate) on Jul 15, 2003 at 10:10 UTC

    I recently wrote a regex to match a floating-point number, including (optional) scientific notatation. This looks like a good chance to get some critique, as well as (possibly) help you. I think it's a bit more complete than the regexen I've seen so far in this post, though I'm sure it's far from perfect.

    /^[+-]?(?=\.?\d)\d*(\.\d*)?([Ee][+-]?\d+)?$/

    HTH

    Update: Well, at least my regex is validated by the Perl Cookbook, as shown in dbp's post. Though I'm curious to know why they used (?=\d|\.\d) instead of (?=\.?\d), when it seems to me the latter is more concise... anyone know of a good reason?

    --
    3dan

Log In?
Username:
Password:

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

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

    No recent polls found