Is it a Number?
*Is it a Number?*

In innumerable postings and email the following question eternally arises:
*
How do I test a string to see if it's a number?
*

I hate this question.
*
I'm sure there's an obvious answer to this,
*

Why yes, there is. I generally like this way:
if ($n == 0) { warn "icky number" }
*
but I haven't yet found or
figured it out. Thanks...
*

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
$answer += 0;

To perform this conversion, Perl merely calls your native C librarys's *atof(3)* 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:
- Your current
**LC_NUMERIC** 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.
- Exponent overflow and underflow produce the results
specified by the IEEE Standard.
- The special IEEE notions of
*Infinity* and
*NaN* (not a number) will be properly 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**

You may also notice that `NaN` is neither
`==`
nor
`!=` 0, which is probably what you want.
If you disagree with the way your vendor has implemented *atof(3)*,
then complain to them, but you'd better be up on your standards docs first.
If you don't like that
*atof(3)* tolerates trailing non-numerics, just cope.
Assuming you don't care about whether something's zero or has trailing garbage, some
slightly simplistic solutions certainly suggest themselves:

do {
print "Number, please: ";
$answer = ;
print "Bad number\n" if $answer == 0;
} until $answer;
If you do care about getting 0's, then do this:
do {
print "Number, please: ";
$answer = ;
if ($answer == 0 && $answer ne '0') {
print "Bad number\n";
}
} until $answer;
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:
do {
print "Number, please: ";
$answer = ;
if ($answer+0 ne $answer) {
print "Bad number\n";
}
} until $answer;
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:

do {
print "Number, please: ";
$answer = ;
local $^W = 0;
if ($answer == 0 && $answer ne '0') {
print "Bad number\n";
}
} until $answer;
If you want to wrap it in a function, do this:
sub bogus_number {
my $potential_number = shift;
local $^W = 0;
my $bogosity = $potential_number == 0
&& $potential_number ne '';
return $bogosity;
}
Hm... one of these days we're going to have deal with this problem of
maybe getting **EOF**. Remember you can't actually test for *eof()* explicitly,
or you'll hose the interactive user.
do {
print "Number, please: ";
exit unless defined ($answer = );
if (bogus_number($answer)) {
print "Bad number\n";
$answer = 0;
}
} until $answer;
You could even be cruel and clobber their input:
sub bogus_number {
local $^W = 0;
if ($_[0] == 0 && $_[0] ne '') {
$_[0] = ''; # squish my caller!
return 1;
}
return 0;
}
do {
print "Number, please: ";
$answer = ;
print "Bad number\n" if bogus_number($answer);
} until $answer;
Or write it the other way:
do {
print "Number, please: ";
$answer = ;
} until nifty_number($answer);
sub nifty_number {
my $potential_number = shift;
local $^W = 0;
my $bogosity = $potential_number == 0
&& $potential_number ne '';
return !$bogosity;
}
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:
sub is_whole_number { $_[0] =~ /^\d+$/ }
sub is_integer { $_[0] =~ /^[+-]?\d+$/ }
sub is_float { $_[0] =~ /^[+-]?\d+\.?\d*$/ }
For a more proper solution,
chew on this
output from an old paper-tape processing machine from Mark Biggar:
sub nifty_number {
$_[0] =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;
}
or written out more legibly:
sub nifty_number {
$_[0] =~ m{ # YANETUT
^ ( [+-]? )
(?= \d
| \.\d
)
\d*
( \. \d* ) ?
( [Ee] ( [+-]? \d+ ) ) ?
$
}x
}
Nearly all of these solutions suffer from problems that the simple
```
$num += 0;
```

has no problems with.

Return to:

Copyright 1996 Tom Christiansen.

All rights reserved.