http://qs1969.pair.com?node_id=110144

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

What is the canonical, quick way to do this? Javascript has isNaN (which, admittedly, checks whether the var is NOT a number, but that's fine). Is it possible to do it with as few characters in perl?

§ George Sherston

Replies are listed 'Best First'.
Re: Checking whether a $var is a number
by rchiav (Deacon) on Sep 05, 2001 at 00:16 UTC
(tye)Re: Checking whether a $var is a number
by tye (Sage) on Sep 05, 2001 at 00:46 UTC

    If I can come up with so many different interpretations of "is this string an integer" ((tye)Re: Integer detection concept), just think how many interpretations of "is this string a number" covers.

    First you need to decide which of the many meanings you are dealing with and then you can decide which of probably several ways you can try to get that answer. If someone offers to tell you how to determine is a string "is a number" without forcing you to be a lot more specific, then are likely answering the wrong question for you. (:

    Though, I do think that Perl's internal looks_like_number() might be the right answer for several common meanings of that question. I really need to find the time to get an interface to that available from CPAN and included in a future version of the core. /:

            - tye (but my friends call me "Tye")
Re: Checking whether a $var is a number
by dragonchild (Archbishop) on Sep 05, 2001 at 00:24 UTC
    The really obvious way to do it is to make sure that the scalar doesn't contain a non-numeric character.
    if ($var && $var !~ /\D/) { # We have a value and it's a number }
    You need the initial check to make sure that $var actually contains something, otherwise the !~ will trigger a warning about unitialized variables.

    So, you can code up a neato little function, sorta like:

    sub isNum { if ($var && $var !~ /\D/) { return 1; } else { return 0; } }
    Then, put that into a module of yours and have that module inherit from Exporter and you're all good! :)

    (Yes, I'm sure someone's already done this, but this was too cool an opportunity to promote module use. *blush*)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Vote paco for President!

      Unfortunatly checking for non-digits is not sufficient since these are all numbers:

      • 12.7
      • 1E0
      • 1.477e7

      Checking just for non-digits won't work on those. And you cannot just check for digits, E, e, and ., since these are all not numbers

      • ...
      • Eeeeeee.
      • 12e
      • e15
      • 1.6.8

      The only thing that your technique will work for is integers -- certainly not the only thing meant by 'number'.

        that's easy to fix (untested):
        qr/(?:+|-)?\d+(?:\.)?(?:\d+)?(?:[Ee](?:+|-)?\d+)/

        piece by piece:
        (?:+|-)? # Optional leading + or - \d+ # One or more digit (?:\.)? # A trailing . 12. is a number I think (?:\d+)? # The part after the decimal (?:[Ee](?:+|-)?\d+) # E or e followed by an optional + or - and one or + more digit
        I don't think I left anything out. Now, this won't match .4 as a number.... You can build a regex that will handle that case properly as well. But the simple fix of making the part before the decimal optional causes another bug, it maks every part optional, so the null string would be a match, and since every string starts with a null string, every string would match. Since all we have is a number, we could anchor to the begenning and end of the value, but then, the empty string would still match. Thinking back to his origonial question, this should be anchored to the front and back, so more like qr/^...$/.
        -Ted
Re: Checking whether a $var is a number
by wog (Curate) on Sep 05, 2001 at 00:15 UTC
    Unfortunately perl does not have any built-in functions to do just this. One way you could check it is by seeing if perl warns "... is not numeric ..." when you try to do something with a value:

    # numeric -- returns true if arg. is numeric sub numeric { my $value = shift; my $result = 1; local $^W = 1; # turn warnings on if they aren't already. local $SIG{__WARN__} = sub { $result = 0 }; # if we get a warning make $result false $value += 0; return $result; }
Re: Checking whether a $var is a number
by John M. Dlugosz (Monsignor) on Sep 05, 2001 at 01:26 UTC
    If you use a value as a number, it is a number.

    A string that can't be parsed as a number at all will be zero. So, how about something like this:

    if ($x==0 && substr($x,0,1) ne '0') { # deal with non-number } else { # deal with number }
    In a real application, you will have a more precise definition of what you want. For example, a spreadsheet formula might be a literal number or a cell reference. For such things, you can write rules to match what your application needs and is documented to accept.

    —John

    Update:fixed typo in substr, reported by dga. Yea, I typed it without checking perlfunc, and I rarely use substr in real code.

      Yes. That (together with a || $x==1 && substr($x,1,1) ne '1') does exactly what I find myself wanting nine times out of ten. That is, what isNaN does in js. I'd find it useful to have a built in function, and maybe we're going to get one.

      § George Sherston
        Hmm, when does a scalar convert to 1 when it's not a number?

        Note that I didn't mention that you should already know that the value is "a string" rather than a reference or funky blessed overloaded object, but carefully worded it because I had that in mind.

      I think you have a typo in your substr which should be

      substr($x,0,1);

      Otherwise the case $x=0; falls into the not a number part of the if.

Re: Checking whether a $var is a number
by Anarion (Hermit) on Sep 05, 2001 at 01:59 UTC
    Nobody said to do $var+=0
    Look here

    $anarion=\$anarion;

    s==q^QBY_^=,$_^=$[x7,print

Re: Checking whether a $var is a number
by hsmyers (Canon) on Sep 05, 2001 at 02:55 UTC
    Just a thought, but if I had to guess, isNaN, probably checks to see if it got a NAN error from the FPU, if so, the quickest way would be to craft your own— but to get your hands on the FPU you would have to go low level (i.e. 'C' or asm) sooner or later...

    Question, does Perl's SIGNAL tie to this exception?

    hsm

Re: Checking whether a $var is a number
by Hofmator (Curate) on Sep 05, 2001 at 13:19 UTC

    Nobody has mentioned the Perl Cookbook yet. Recipe 2.1 discusses this topic, I quote here the regex for a C float (does not take IEEE 'NaN' and 'Infinity' values into account) and another suggested way using a wrapper around the POSIX function strtod:

    warn "not a C float" unless /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/; sub getnum { use POSIX qw(strtod); my $str = shift; $str =~ s/^\s+//; $str =~ s/\s+$//; $! = 0; my($num, $unparsed) = strtod($str); if (($str eq '') || ($unparsed != 0) || $!) { return; } else { return $num; } }

    -- Hofmator

Re: Checking whether a $var is a number
by astaines (Curate) on Sep 05, 2001 at 13:57 UTC
Re (tilly) 1: Checking whether a $var is a number
by tilly (Archbishop) on Sep 06, 2001 at 00:22 UTC
    One issue that nobody has yet mentioned is the possibility of having objects that work as numbers thanks to overload. I guarantee that all of the regex solutions will fail miserably on this because they coerce to a string, not a number. These may be (as in the case of $!) radically different things...

    Without a hook to the internal API, the only real solution is to try to use it as a number and see if Perl complains.

Re: Checking whether a $var is a number
by hsmyers (Canon) on Sep 06, 2001 at 02:03 UTC
    The canonical answer to IsNan is found here: http://www.cpan.org/doc/FMTEYEWTK/is_numeric.html

    To Quote:

    The special IEEE notions of Infinity and NaN (not a number) will be pr +operly honored: $n = 'NaN'; print 2 * $n; NaN print 10 * $n NaN print 1 + NaN NaN $i = 'Infinity' print 1 + $i Infinity print $i * $i Infinity print $i - $i NaN print 'Infinity' < 0 0 print 'Infinity' > 0 1

    hsm

    Edit by tye to change very wide BLOCKQUOTE+PRE to CODE

      Interesting, it looks like this works, but they return 'inf' and 'nan' instead of the longer capitalized versions above.
      % perl -le "print 1 + 'NaN'" nan % perl -le "print 1 + 'Infinity'" inf

      -Blake

Re: Checking whether a $var is a number
by George_Sherston (Vicar) on Sep 12, 2001 at 19:40 UTC
    Update for benefit of posterity: a similar thread to this one is currently running here and I thought future generations might find a cross-reference useful, if they get to this point and would still like to see a bit more mind boggling...

    § George Sherston