<CENTER>
<TITLE>Is it a Number?</TITLE>
<H1><I>Is it a Number?</i></h1>
</CENTER>
In innumerable postings and email the following question eternally arises:
<BLOCKQUOTE><i>
How do I test a string to see if it's a number?
</i></BLOCKQUOTE>
I hate this question.
<BLOCKQUOTE><I>
I'm sure there's an obvious answer to this,
</I></BLOCKQUOTE>
Why yes, there is. I generally like this way:
<XMP>
if ($n == 0) { warn "icky number" }
</XMP>
<BLOCKQUOTE><I>
but I haven't yet found or
figured it out. Thanks...
</I></BLOCKQUOTE>
Why do you want to see whether it's a number? Perl is happy
to use strings and numbers interchangeably. People make way too
much trouble of this. Normally, all you want to do is
<PRE>
$answer += 0;
</PRE>
To perform this conversion, Perl merely calls your native C librarys's <I>atof(3)</I> function.
Many important benefits come from this approach, which if you try to roll it yourself,
you'll probably miss. Here are some of them:
<OL>
<LI>Your current <B><font size=-1>LC_NUMERIC</font></B> locale setting is used and respected.
This means that the user's local radix will be respected. For example,
if you're in a country where the USA's notion of comma and period
are switched, the right thing will still happen.
<LI>Exponent overflow and underflow produce the results
specified by the <font size=-1>IEEE</font> Standard.
<LI>The special <font size=-1>IEEE</font> notions of
<I>Infinity</I> and
<I>NaN</I> (not a number) will be properly honored:
<PRE><CODE>
$n = 'NaN';
print 2 * $n;
<B>NaN</B>
print 10 * $n
<B>NaN</B>
print 1 + NaN
<B>NaN</B>
$i = 'Infinity'
print 1 + $i
<B>Infinity</B>
print $i * $i
<B>Infinity</B>
print $i - $i
<B>NaN</B>
print 'Infinity' < 0
<B>0</B>
print 'Infinity' > 0
<B>1</B>
</CODE></PRE></OL>
You may also notice that <TT>NaN</TT> is neither
<TT>==</TT>
nor
<TT>!=</TT> 0, which is probably what you want.
If you disagree with the way your vendor has implemented <I>atof(3)</I>,
then complain to them, but you'd better be up on your standards docs first.
If you don't like that
<I>atof(3)</I> tolerates trailing non-numerics, just cope.
<P>
Assuming you don't care about whether something's zero or has trailing garbage, some
slightly simplistic solutions certainly suggest themselves:
<XMP>
do {
print "Number, please: ";
$answer = <STDIN>;
print "Bad number\n" if $answer == 0;
} until $answer;
</XMP>
If you do care about getting 0's, then do this:
<XMP>
do {
print "Number, please: ";
$answer = <STDIN>;
if ($answer == 0 && $answer ne '0') {
print "Bad number\n";
}
} until $answer;
</XMP>
A related approach is to see whether the lexical and numeric
representations are the same. This solution is often
by those who don't like trailing non-digits in their numbers:
<XMP>
do {
print "Number, please: ";
$answer = <STDIN>;
if ($answer+0 ne $answer) {
print "Bad number\n";
}
} until $answer;
</XMP>
<P>
If you find yourself unduly annoyed from being chidden about improper numeric
conversions, as I'm sure I'm about to be, just do something like this:
<XMP>
do {
print "Number, please: ";
$answer = <STDIN>;
local $^W = 0;
if ($answer == 0 && $answer ne '0') {
print "Bad number\n";
}
} until $answer;
</XMP>
If you want to wrap it in a function, do this:
<XMP>
sub bogus_number {
my $potential_number = shift;
local $^W = 0;
my $bogosity = $potential_number == 0
&& $potential_number ne '';
return $bogosity;
}
</XMP>
Hm... one of these days we're going to have deal with this problem of
maybe getting <B><font size=-1>EOF</font></B>. Remember you can't actually test for <I>eof()</I> explicitly,
or you'll hose the interactive user.
<XMP>
do {
print "Number, please: ";
exit unless defined ($answer = <STDIN>);
if (bogus_number($answer)) {
print "Bad number\n";
$answer = 0;
}
} until $answer;
</XMP>
You could even be cruel and clobber their input:
<XMP>
sub bogus_number {
local $^W = 0;
if ($_[0] == 0 && $_[0] ne '') {
$_[0] = ''; # squish my caller!
return 1;
}
return 0;
}
do {
print "Number, please: ";
$answer = <STDIN>;
print "Bad number\n" if bogus_number($answer);
} until $answer;
</XMP>
Or write it the other way:
<XMP>
do {
print "Number, please: ";
$answer = <STDIN>;
} until nifty_number($answer);
sub nifty_number {
my $potential_number = shift;
local $^W = 0;
my $bogosity = $potential_number == 0
&& $potential_number ne '';
return !$bogosity;
}
</XMP>
Someone is going to ask the question ``Can't I use a regular expression do
this?'' Why, yes, Virginia, you may, and don't say can. :-) Actually, regular
expressions
are the general way one verifies input in Perl. Here are some
simple-minded schemes for detecting such things:
<XMP>
sub is_whole_number { $_[0] =~ /^\d+$/ }
sub is_integer { $_[0] =~ /^[+-]?\d+$/ }
sub is_float { $_[0] =~ /^[+-]?\d+\.?\d*$/ }
</XMP>
For a more proper solution,
chew on this
output from an old paper-tape processing machine from Mark Biggar:
<XMP>
sub nifty_number {
$_[0] =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;
}
</XMP>
or written out more legibly:
<XMP>
sub nifty_number {
$_[0] =~ m{ # YANETUT
^ ( [+-]? )
(?= \d
| \.\d
)
\d*
( \. \d* ) ?
( [Ee] ( [+-]? \d+ ) ) ?
$
}x
}
</XMP>
Nearly all of these solutions suffer from problems that the simple
<CODE>
$num += 0;
</CODE>
has no problems with.