1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

PHPの数値判定を色々なis...関数で試した結果 (一覧表あり)

Last updated at Posted at 2024-06-12

各種is_...関数の挙動の比較

PHPで入力値や計算値が数値かどうかを判断するときにどの関数を使ったらいいの? と思って調べたのだが、明確な答えが見つからなかったので自分でやってみた。結果、想像していた通りになったりならなかったりして面白かったので紹介したい。エントリーメンバーはこちら。

  1. is_nan(float $num): bool
    値がNANであればTRUEを返す、と言われるけれども、NANそのものが「(float型の)数」なのに「Not A Number(非数)」という難解な説明。今回は数値かどうかの判断なので!is_nan()で検証。引数にfloatでない値(例えば文字列)を入れるとエラーを出す(はずなのだが条件がある。後述)。
  2. is_numeric(mixed $value) :bool
    「数か数値形式の文字列」(≒数っぽい値)であればTRUEを返す、という<form>の値を受ける時毎度お世話になる関数1
  3. is_int(mixed $value) :bool
    マニュアル通りに解釈すると「変数が整数型であればTRUE」となるが、mixedを引くので文字列を入れてもエラーにはならずFALSEを返す。
  4. is_float(mixed $value :bool
    上のis_int()と逆に「変数が浮動小数点型であればTRUE」。ちなみにPHP_INT_MAXを超える数や指数表記の数は結果が整数であっても浮動小数点型になる。
  5. is_finite(float $num) :bool
    数値判定を何でやれば…?をググってよく出てくるのはコレ2。「有限の浮動小数点値であればTRUE」であり、「有限の浮動小数点値」とは「浮動小数点がNANでなくかつ無限(INF)でもない」とマニュアルにはあるが、じゃあ整数値を入れるとどうなるのか、という疑問が残る。

さて、この関数群に様々な値をぶち込んだ結果がこちら。

数の説明 !is_nan  is_numeric is_int   is_float  is_finite 
0 数値のゼロ TRUE TRUE TRUE FALSE TRUE
10**100 PHP_INT_MAXを
超えるint
TRUE TRUE FALSE TRUE TRUE
sqrt(-1) √-1(虚数) FALSE TRUE FALSE TRUE FALSE
tan(pi()/2) tan(π/2)(数学で
は未定義)
TRUE TRUE FALSE TRUE TRUE
log(0) 0の自然対数(数
学では未定義)
TRUE TRUE FALSE TRUE FALSE
1.5E2 intの範囲の
指数表記
TRUE TRUE FALSE TRUE TRUE
1.5E10000 64bitの限界を
超える指数表記
TRUE TRUE FALSE TRUE FALSE
0b1101101 int(2進表記) TRUE TRUE TRUE FALSE TRUE
0155 int(8進表記) TRUE TRUE TRUE FALSE TRUE
0x6D int(16進表記) TRUE TRUE TRUE FALSE TRUE
'0' 文字列のゼロ TRUE TRUE FALSE FALSE TRUE
'1.5E2' 指数表記の
文字列
TRUE TRUE FALSE FALSE TRUE
'0155' 8進表記の
文字列
TRUE TRUE FALSE FALSE TRUE
'0x6D' 16進表記の
文字列
ERROR FALSE FALSE FALSE ERROR
INF 無限大 TRUE TRUE FALSE TRUE FALSE
NAN 非数 FALSE TRUE FALSE TRUE FALSE

以下、結果を見てあれ? と思ったところ。

  1. is_finite(0) : TRUE
    一見すると正しいが、0は浮動小数点値ではない(その証拠にis_float(0) : FALSEである)のに、is_finite()の定義「有限の浮動小数点値であればTRUE」から見ると結果が正しくない。むしろ定義のほうを「整数値または有限の浮動小数点値であればTRUE」とするべきであろうか。
  2. is_float(sqrt(-1)) : TRUE
    is_nan(sqrt(-1)) : TRUENANの例としてよく挙げられているが、これは「非数というfloatの一種」という意味らしい。実際var_dump(sqrt(-1))の返り値はfloat(NAN)である。
  3. is_finite(tan(pi()/2)) : TRUE
    数学でのtan(π/2)は0除算を含むため未定義であるが、PHPではエラーが出ないどころかINFにすらならずis_finite()!is_nan()TRUEになる3。このタネはtan(pi()/2)の値にあって、var_dump(tan(pi()/2))を実行するとなんとfloat(16331239353195370)という値4が返ってくる。要するに単なる有限値のfloatと判定されるようだ。
  4. is_finite(log(0)) : FALSE
    上と同じ数学上の未定義値だがなぜかこちらはis_finite()FALSE!is_nan()TRUE。これも種明かしするとvar_dump(log(0)) : -INF、無限(INF, -INF)は非数ではないのでこの結果になる。
  5. !is_nan('0') : TRUE
    is_nan()は引数に文字列を入れるとTypeErrorを出すはずなのになぜか'0'はそのまま通してしまった。同様のことがis_finite()でも起こっている。後の'1.5E''0155'を見るに「数値形式の文字列」(is_numeric()TRUEを返す値)であれば数値と解釈してくれるようだ5

数値と文字列を厳密に区別する方法

ところで'0'を数値としたくない(0と'0'を区別したい)ときに、2つ思いついたのを同じように比較してみた。

is_int(\$a) || is_float(\$a) intval(\$a)===\$a || floatval(\$a)===\$a
0 TRUE TRUE
10**100 TRUE TRUE
sqrt(-1) TRUE FALSE
tan(pi()/2) TRUE TRUE
log(0) TRUE TRUE
1.5E2 TRUE TRUE
1.5E10000 TRUE TRUE
0b1101101 TRUE TRUE
0155 TRUE TRUE
0x6D TRUE TRUE
'0' FALSE FALSE
'1.5E2' FALSE FALSE
'0155' FALSE FALSE
'0x6D' FALSE FALSE
INF TRUE TRUE
NAN TRUE FALSE

どちらも数値形式の文字列はちゃんと弾いてくれる。また、上で述べたようにsqrt(-1)NANなのだが、phpマニュアルのis_nanの項目の警告として

NAN は、 NAN と等しい値として比較できません。 浮動小数点数の値が NAN かを判定するには is_nan() を使わなければいけません。 コード $float === NAN は動作しません。

とあるようにfloatval(NAN) === NANFALSEである一方、is_float(NAN)TRUEなのでこの部分だけ結果が異なる。

思いつきでやってみたけど、いろいろ勉強になった。

  1. PHPは周知の通り型判定がゆるいので$a = '5'; $b = 6; var_dump($a * $b) : int(30)とかいう、他言語ではありえない結果になる。しかし例えばJavaScriptなどではそうならないため、intval()をかまさないと思ったようにならなかったりする(何度ハマったことか…)。

  2. 英語の「finite」は「ファイナイト」と発音するので念の為。infiniteは「インフィニット」である。

  3. is_nan(1/0)is_infinite(1/0)を実行するとDivisionByZeroErrorになりTRUEFALSEも返さない。

  4. この妙な結果は、正接tan(α)をsin(α)/cos(α)によって算出していることから来ているようだ。円周率が途中で丸められてしまうためcos(π/2)=0.000 000 000 000 000 061 232 339 957 367 66となり(0にはならない)、これでsin(π/2)=1を割ると17桁の謎整数が出てくるらしい。なおJavaScriptでMath.tan(Math.PI()/2)を実行しても同じ結果になる。

  5. これはマニュアルには書かれていない。なお定義上の「数値形式の文字列」に指数(E表記)は含まれているが2進・16進表記は含まれていないので、is_numeric('0x6D')FALSEとなり、!is_nan('0x6D')TypeErrorになるという仕様。

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?