詳しくはCookbookの2.1を参照してもらうとして、これは以外とよく出てくる設問なので。

Kazuho@Cybozu Labs: MySQL の高速化プチBK
ところで、実数の場合はどうすればいいんでしょうね。

Scalar::Util::looks_like_number()

Perl 5.8.1以降であれば、第一選択はこれです。第一引数の結果が数値か否かを、Perl APIに直接問い合わせるので高速です。ベンチマークを最後に示しますが、真偽ともに正規表現と比較して250倍高速です。

Regexp::Common$RE{num}{real}

それ以外であれば、Regexp::CommonをCPANからインストールして、そこで定義される$RE{num}{real}を使うのがよいでしょう。

生の正規表現

これもCookbook(英語版2nd ed.)のp. 61に載っています。Perl以外でも使えるので、少し手直しした上で掲載。

sub isnt_a_number        { $_[0] =~ /\D/ }
sub is_a_natual_number   { $_[0] =~ /\A\d+\z/ }
sub is_an_integer        { $_[0] =~ /\-?\d+\z/ }
sub is_an_integer2       { $_[0] =~ /\A[\+\-]?\d+\z/ } # +3
sub is_a_decimal_number  { $_[0] =~ /\A[\+\-]?\d+\.?\d*\z/ }
sub is_a_decimal_number2 { $_[0] =~ /\A[\+\-]?(?:\d+(?:\.\d*)?|\.d+)\z/ }
sub is_a_C_float         { $_[0] =~ /\A([\+\-])?(?=\d|\.d)\d*(\.\d*)?(([Ee])([\+\-]?\d+))?\z/ }

整数化して判断

実は、これが一番高速だったりします。

sub is_a_num { $_[0] != 0 }

ただし、この場合数値の0は数値と見なされません。0まできちんとチェックしたい場合は、

sub is_a_num { $_[0] != 0 or $_[0] =~ /\A0(?:\.0+)\z/ }

とすればよいでしょう。ただし、この場合"0 but true"のような場合など、NGのケースもあります。Scalar::Util::looks_like_number()が使える場合はそちらの方がよいでしょう。

Enjoy!

Dan the Number-Crunching Perl Monger

use strict;
use warnings;
use Scalar::Util qw/looks_like_number/;
use Regexp::Common;
use Benchmark qw/cmpthese timethese/;

my %posneg = (
              pos => 6.0221415e23,
              neg => "Avogadro's Number",
  ) ;

for my $pn ( keys %posneg ) {
    my $su =
      $pn eq 'pos'
      ? sub { looks_like_number( $posneg{$pn} ) or  die }
      : sub { looks_like_number( $posneg{$pn} ) and die };
    my $rc =
      $pn eq 'pos'
      ? sub { $posneg{$pn} =~ /\A$RE{num}{real}\z/ or  die }
      : sub { $posneg{$pn} =~ /\A$RE{num}{real}\z/ and die };
    my $n0 = do {
        no warnings 'numeric';
          $pn eq 'pos'
          ? sub { $posneg{$pn} !=0 or  die }
          : sub { $posneg{$pn} !=0 and die };
    };
    my $n1 = do {
        no warnings 'numeric';
        $pn eq 'pos'
          ? sub {
              ( $posneg{$pn} != 0 or $posneg{$pn} =~ /\A0(?:\.0+)\z/ )
              or die;
          }
          : sub {
              ( $posneg{$pn} != 0 or $posneg{$pn} =~ /\A0(?:\.0+)\z/ )
              and die;
          };
    };
    cmpthese(timethese(0,{
                          "S:U    $pn"  => $su,
                          "R:C    $pn"  => $rc,
                          "numify $pn"  => $n0,
                          "num+rx $pn"  => $n1,
                         }
                      ));
}
Benchmark: running R:C    pos, S:U    pos, num+rx pos, numify pos for at least 3 CPU seconds...
R:C    pos:  3 wallclock secs ( 3.21 usr +  0.01 sys =  3.22 CPU) @ 8217.39/s (n=26460)
S:U    pos:  3 wallclock secs ( 3.05 usr +  0.02 sys =  3.07 CPU) @ 2277608.47/s (n=6992258)
num+rx pos:  3 wallclock secs ( 3.09 usr +  0.01 sys =  3.10 CPU) @ 4921243.87/s (n=15255856)
numify pos:  5 wallclock secs ( 3.07 usr +  0.02 sys =  3.09 CPU) @ 5526500.97/s (n=17076888)
                Rate R:C    pos S:U    pos num+rx pos numify pos
R:C    pos    8217/s         --      -100%      -100%      -100%
S:U    pos 2277608/s     27617%         --       -54%       -59%
num+rx pos 4921244/s     59788%       116%         --       -11%
numify pos 5526501/s     67154%       143%        12%         --
Benchmark: running R:C    neg, S:U    neg, num+rx neg, numify neg for at least 3 CPU seconds...
R:C    neg:  3 wallclock secs ( 3.15 usr +  0.01 sys =  3.16 CPU) @ 8370.25/s (n=26450)
S:U    neg:  4 wallclock secs ( 3.07 usr +  0.02 sys =  3.09 CPU) @ 2262869.90/s (n=6992268)
num+rx neg:  4 wallclock secs ( 3.15 usr +  0.01 sys =  3.16 CPU) @ 2541496.52/s (n=8031129)
numify neg:  0 wallclock secs ( 3.15 usr +  0.00 sys =  3.15 CPU) @ 6017865.08/s (n=18956275)
                Rate R:C    neg S:U    neg num+rx neg numify neg
R:C    neg    8370/s         --      -100%      -100%      -100%
S:U    neg 2262870/s     26935%         --       -11%       -62%
num+rx neg 2541497/s     30263%        12%         --       -58%
numify neg 6017865/s     71796%       166%       137%         --