こんにちわ、PHPエンジニアのエンジニア婦人(@naho_osada)です。
PHPエンジニアとして9年~の経験があります。
今回はPHPのsprintf関数についての使い方と、使い方の注意です。
整形してくれてとても便利、四捨五入などもお手の物!のように見えますが、実はsprintfは数値を厳密には扱えないのです。
sprintfの使い方の基本
フォーマットされた「文字列を返す」PHP関数です。
たとえば、数字の先頭を0埋めしたいとき。
sprintf("%02d", $num)
などと使います。
便利なsprintfですが、これを使うとき、注意しなければならないことがあります。
それは「四捨五入のためにsprintfを使ってはならない」ことです。
以下は実際にあった話です。やらかした話です。
小数点一桁表示にするときに使ったらバグ
あるとき、数字を表示することがありました。
そのときに「何桁まで出しますか?」と聞いて「小数点第一位表示で」とのことでした。
その表示ならフォーマット整形、sprintfでいいなと思って実装しました。
sprintf("%0.1f", 小数点以下のある何かの数値);
こんな感じになります。
数値は小数点第二位を四捨五入した小数点第一位までの表示して、特別問題がなさそうに見えました。
しかし、検証を続けていくとこちらの想定通りに四捨五入されない場合があることがわかりました。
echo sprintf("%0.1f", 2.24);
echo sprintf("%0.1f", 2.25);
echo sprintf("%0.1f", 2.26);
これを実行してみるとわかりますが…四捨五入じゃなくて、五捨六入されています…!
公式にも「四捨五入する」とは書いてない
PHP公式のsprintfのページをみてみると、四捨五入するとは書いていませんでした。
ずっと四捨五入はsprintfの0.1fでもOKと思い込んでいたのだけれど、一体どこで記憶違いしたんだろう。Perlかな(でもPerlもroundあるよね)
…これまでやってきたどこかで、思い違いをしていたようです。
五捨六入になっていた考えられる原因
恐らく浮動小数点の関係でずれてしまうのだと思われます。内部的には四捨五入しているのかしら。
数値の取り扱いはround/floor/ceilを使う
本件の場合はroundを使って処理をすることで問題がなくなりました。
echo round(2.24, 1);
echo round(2.25, 1);
echo round(2.26, 1);
roundで丸めます。整形の必要があるなら、数値処理をしたあとにsprintfを使用します。
まとめ
厳密な数値を扱うときに、sprintfは使ってはいけません。ずれます。
round / floor / ceilを目的によって使い分けましょう。
数字で使うときはしかるべき関数(round / floor / ceil)を使ったのちに使用しましょう。