PHPがシンプルで柔軟性があり、要求の厳しい言語ではないことは、Web開発者にとって秘密ではありません。 しかし、この言語で作業する場合、予期しないことに出くわす可能性があります。 この記事では、「奇妙な事実」を提示し、PHPがこのような結果をもたらす理由を説明します。
浮動小数点の不正確さ
ほとんどの人は、浮動小数点数がすべての実数を実際に表すことができないことをおそらく知っています。 さらに、2つの一見明確に定義された数値間の一部の操作は、予期しない状況につながる可能性があります。 これは、コンピューターが数値を格納する精度に独自の特性があるためです。 この現象は、PHPだけでなく、すべてのプログラミング言語にも影響します。 浮動小数点演算の不正確さは、そのような分野の創設以来、プログラマにかなりの頭痛の種を引き起こしました。
この事実はすでに何度も書かれています。 最も重要な記事の1つは、「すべてのコンピュータースペシャリストが浮動小数点演算について知っておくべきこと」です。 一度も読んだことがない場合は、これを修正することを強くお勧めします。
この小さなコードを見てみましょう。
<?php<br/>
echo (int) ((0.1 + 0.7) * 10);
結果はどうなると思いますか? 操作の結果が8であると仮定すると、間違えられます。 実際、7。Zend証明書を持っている人、この例はすでに知られています。 ところで、この例はZend Certification Preparation Guideにあります。
では、なぜこれが起こるのか見てみましょう。
0.1 + 0.7 = 0.79999999999
この操作の結果は、実際には0.8でなく0.79999999999として保存されます。 これが問題の始まりです。
実行する2番目の操作:
0.79999999 * 10 = 7.999999999
この操作は正常に機能しますが、問題は残ります。
最後に、3番目の最後の操作:
(int) 7.9999999 = 7
この式は明示的なキャストを使用します。 値がintにキャストされると、PHPは小数部分を切り捨て、最終的に7を返します。
- この式をintではなくfloatにキャストした場合、またはまったくタイプキャストしない場合、期待どおり8という数字が得られます
- これは、0.999 ...が1に等しい数学的なパラドックスがあるという事実のためであり、このエラーが発生しました。
PHPが文字列を「インクリメント」する方法
操作中は、常にデータに似た増分/減分操作を使用します。
<?php
$a = 5;
$b = 6;
$a++;
++$b;
ここで何が起こっているのか、私たち一人一人は簡単に理解できます。 しかし、このコードが何を出力するのかを理解してください:
<?php
$a = 1;
$b = 3;
echo $a++ + $b;
echo $a + ++$b;
echo ++$a + $b++;
見てみましょう:
4
6
7
それほど複雑ではありませんか? 次に、複雑さを少し増やしましょう。 以前に文字列をインクリメントしようとしたことがありますか? このコードが出力すると仮定してみてください:
<?php
$a = 'fact_2';
echo ++$a;
$a = '2nd_fact';
echo ++$a;
$a = 'a_fact';
echo ++$a;
$a = 'a_fact?';
echo ++$a;
このタスクはすでに困難です。 私たちが得たものを見てみましょう:
fact_3
2nd_facu
a_facu
a_fact?
びっくりした? 数字で終わる行をインクリメントすることで、実際に文字を増やします(アルファベット順で次の文字、つまりtがuに続く後)。 行が数字で始まるかどうかに関係なく、最後の文字が変更されます。 ただし、文字列が英数字以外の文字で終わる場合、この操作は意味がありません。
この点は、インクリメント/デクリメント操作に関する公式ドキュメントに詳しく説明されていますが、多くの人はそこで特別なものを期待していないため、この資料を読みませんでした。 私は最近までまったく同じことを考えていたことを認めたいと思います。
意味の秘密
あなたはPHPの配列マスターです。 それについて恥ずかしがらないでください。 アレイの作成、編集、削除に関するすべてをすでに知っています。 ただし、次の例は驚くかもしれません。
非常に頻繁に配列を操作する場合、配列内で何かを探す必要があります。 PHPには、このin_array()用の特別な関数があります。 彼女の動作を見てみましょう。
<?php
$array = array(
'isReady' => false,
'isPHP' => true,
'isStrange' => true
);
var_dump(in_array('phptime.ru', $array));
何を推測すべきですか?
true
それは少し奇妙ではありません。 ブール値のみを含む連想配列があり、文字列を検索するとtrueになります。 それは本当に魔法ですか? 別の例を見てみましょう:
<?php
$array = array(
'count' => 1,
'references' => 0,
'ghosts' => 1
);
var_dump(in_array('aurelio', $array));
そして、私たちは何を得ますか?
true
そして再びin_array()はtrueを返しました。 これはどのように可能ですか?
お気に入りのPHP関数の1つを同時に使用しました。 PHPは厳密に型指定された言語ではないことを言わなければなりません。 多くの問題はまさにこのためです。 実際、PHPで比較した場合、単一の比較演算子を使用すると、次の値はすべて同一になります。
0
false
""
"0"
null
array()
デフォルトでは、in_array()は「柔軟な」比較を使用するため、空ではない( "")および非ゼロの文字列( "0")はtrueと同等であり、すべての非ゼロ要素(1など)に適用されます。 したがって、最初の例では 'phpmaster.com' == trueであるため、trueになりました。2番目の例では 'aurelio' == 1です。
この問題を解決するには、in_array()関数で3番目の追加パラメーターを使用する必要があります。これにより、要素の厳密な比較が可能になります。 今書いたら:
<?php
$array = array(
'count' => 1,
'references' => 0,
'ghosts' => 1
);
var_dump(in_array('aurelio', $array, true));
偽りになります。
おわりに
この記事では、PHPインタープリターの奇妙で予想外の動作を見ました。 読んだものから学べることは次のとおりです。
- 浮動小数点数を信頼しないでください。
- 使用する前にデータ型を再確認してください。
- 「柔軟な」比較と「柔軟な」タイプの問題に注意してください。
あなたが上級プログラマーであれば、おそらくこれらの奇妙なものの存在を知っていた可能性が高いでしょうが、繰り返しは決して価値がありません。