1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

13: 比較演算子, 厳密な等価演算子

Posted at

比較演算子

左辺と右辺の値を比較し、その結果をtrue/falseとして返す。(下記)
比較演算子は、if, while, do〜whileのような条件分岐/繰り返し命令で条件式を表すために利用する。

主な比較演算子

演算 概要 用例
== 左辺と右辺の値が等しい場合はtrue 7 == 7 →true
=== 左辺と右辺の値が等しく、かつ、同じデータ型である場合はtrue 7 === ‘7’ →false
!= 左辺と右辺の値が等しくない場合にtrue 7 != 7 →false
<> 左辺と右辺の値が等しくない場合にtrue(「!=」と同じ) 7 <> 7 →false
!== 左辺と右辺の値が等しくない、または同じデータ型でない場合にtrue 7 !== ‘7’ →true
< 左辺が右辺より小さい場合にtrue 7 < 10 →true
> 左辺が右辺より大きい場合にtrue 7 > 10 →false
<= 左辺が右辺以下の場合にtrue 7 <= 10 →true
>= 左辺が右辺以上の場合にtrue 7 >= 10 →false
<=> 宇宙船演算子。左辺が右辺より小さい場合には-1、左辺と右辺が等しい場合は0、左辺が右辺より大きい場合は1 7 <=> 10 → -1
?: 条件演算子。「条件式? 式1 : 式2」。条件式がtrueの場合は式1、falseの場合は式2 (10 > 1)?’正しい’ : ‘間違い’ →正しい
?? null合体演算子。左辺がnullでなければその値、左辺がnullならば右辺の値、左辺も右辺もnullの場合はnull null ?? 10 →10

比較演算子は、代数演算子と並んで直感的に理解しやすい演算子だが、時として、その自由度のために複雑な動きをするので要注意。

文字列混在の比較

等価演算子「==」は、数値と文字列を比較するとき、文字列を数値に変換した上で比較しようとする。

また、文字列同士の比較であっても、数値形式の文字列である場合には、同じく数値に変換したものを比較しようとする。

具体例(下記)

なお、文字列混在の比較についてはPHP8で変化したので、まずはPHP7.4までの結果から↓

equal.php

var_dump(‘3.14’ == ‘3.14000’);  //結果: bool(true) ①
var_dump(‘3.14E2’ == 3.14);     //結果: bool(true) ②
var_dump(‘0x10’ == 16);         //結果: bool(false) ③
var_dump(‘010’ == 8);           //結果: bool(false) ④
var_dump(‘0b11’ == 3);          //結果: bool(false) ⑤
var_dump(‘13xyz’ == 13);        //結果: bool(true) ⑥
var_dump(‘X’ == 0);             //結果: bool(true) ⑦

//////////①〜⑦文字列と数値との比較/////////

var_dump(‘3.14’ == ‘3.14000’);  //結果: bool(true) ⑧
var_dump(‘3.14E2’ == ‘3.14’);   //結果: bool(true) ⑨
var_dump(‘13xyz’ == ‘13’);      //結果: bool(false) ⑩

//////////⑧〜⑩文字列同士の比較////////////

①〜⑦は文字列と数値の比較?’3.14’(①)のような小数点数はもちろん、’3.14E2’(②)のような指数表記も、浮動小数点リテラルとみなされて、正しく比較されている。

ただし、代数演算子でもそうだったように、2/8/16進数リテラルは正しく認識されない点に要注意(③〜⑤)。

‘0x10’、’0b11’は文字列以降が切り捨てられた結果、0とみなされるし、’010’はそのまま10と認識されるので、いずれも条件式はfalseとなる。

⑥のような数値/文字列混在の文字列では、先頭の数値だけが認識されるのだった。
このため、’13xyz’は13とみなされ、「’13xyz’ == ‘13’」はtrueとなる。

同じ理由で⑦のようなケースでも’X’は数値に変換するとゼロなので(先頭部分に数値が無い為)、「’X’ == 0」はtrueと評価される。

文字列同士の比較も見てみる(⑧〜⑩)。⑧と⑨については問題ない。
純粋な数値文字列であれば、数値に変換されてから比較されるので「’3.14’ == ‘3.14000’」も「’3.14E2’ == ‘314’」もtrue。

しかし⑩だけは例外。
文字列同士の比較で片方が純粋な数値文字列でない場合、数値に変換されない。
したがって「’13xyz’ == ‘13’」は異なる文字列とみなされて、結果はfalseとなる。



厳密には、浮動小数点数を直接に演算(比較)するのは正しくない。ここでは「==」演算子の性質を確認するために、あえて浮動小数点数を例としたが、一般的にはこのようなコードを書くべきではない。

*** ***

PHP8での挙動

PHP8では、⑥、⑦の挙動が変化し、いずれもfalseとなる。

バージョン8にもなって比較ルールが変化するのも驚きだが、’13xyz’か13、ましてや’X’が0というのも直感的に理解しにくいルールだったので、これが影響するアプリもほぼ無い。

厳密な等価演算子(===)

ここまでの説明でも分かる通り、要は「==」演算子では オペランドのデータ型が異なる場合にも、データ型を変換して「なんとか等しいとみなせないか」と試みる親切な演算子。

もっとも親切は時としてお節介にもなり得る。

たとえばequal.phpの例を見ても、「’13xyz’ == 13」はtrue、「’13xyz’ == ‘13’」はfalse、というのはいかにもわかりにくい挙動だし、「’X’ == 0」がtrueというのも、多くの場合は期待しない結果である。

これらの問題はPHP8以降では解消されているが、他にも「’3.14E2’ == 314」がtrueという結果は、場合によっては、期待しない結果かもしれない。(’3.14E2’は指数表現でなく、’3.14E2’という文字列リテラルであるかもしれないから)。
そもそもバージョンによって結果が異なる演算子を無条件に利用するべきでは無い。

以上のような理由から、特に文字列同士の比較では、できるだけ「===」演算子を利用することをお勧めする。

「===」演算子は「厳密な等価演算子」とも呼ばれ、値を比較する際に値とデータ型が厳密に一致するかどうかを判定する。

<?php
var_dump(‘3.14E2’ === 314); //結果: bool(false)①

var_dump(‘X’ === 0); //結果: bool(false)②

var_dump(‘1’ === 1); //結果: bool(false)③

「===」演算子では、文字列が数値へと勝手に変換されることはないので、「==」演算子ではtrueと評価されていたようなケースもfalseとなる(①、②)。

ただし、「===」演算子では、一見、同じ値に見えるリテラルも異なるものとみなされてしまうので要注意。

③では左辺/右辺ともに「1」ですが、左辺は’1’とクォートで囲まれているので文字列リテラル、右辺は単なる1なので整数リテラルとみなされる。

そのため、「===」演算子は両者を違うものと判断してfalseを返す。

PHPはデータ型に寛容な言語だが、データ型を持たない言語ではない。

14: は浮動小数点数の比較から

1
0
1

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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?