📝

PHPで浮動小数点数を文字列に正しくキャストする

2023/04/01に公開

ある日のこと

PHPでfloatの値を BCMath 関数 で計算するために、文字列にキャストしようとしました。

$num = 0.00001;
var_dump((string) $num);
出力結果
string(6) "1.0E-5"

おや?様子がおかしいぞ...
ということで何が起こっているのか調べてみました。

よりよい方法やバグ等ございましたら、アドバイスいただけると光栄です。

何が起こっているのか

どうやらサーバー上で数字を表示する場合、小数点表記で表示できる値に制限があり
0.0001 未満になると指数表記に変換されてしまうとのこと。

なので、下記のように実行すると

var_dump(0.0001);
var_dump(0.00009);
var_dump(0.00001);

そもそもfloatの時点で指数表記になっている。

出力結果
float(0.0001)
float(9.0E-5)
float(1.0E-5)

文字列にキャストすると、上記の値がそのまま文字列に変換されるので
小さな値の場合は指数表記で出力されてしまう。

出力結果
string(6) "0.0001"
string(6) "9.0E-5"
string(6) "1.0E-5"

どうすれば正しくキャストできるのか

sprintf関数 を使って以下ように変換すればOK。

var_dump(sprintf('%.5F', 0.00001));
出力結果
tring(7) "0.00001"

フォーマット文字列である %.5F は、以下のような意味を持つ。

文字列 意味
% フォーマットの接頭辞
.5 ピリオド(.)の後に小数点の後ろに表示する桁数を入力
F 引数を小数として扱い、ロケールを考慮せずに浮動小数点数値として出力

おまけ

sprintf関数 のフォーマットでロケールを考慮するかどうかみたいな話が出てきたので
何が違うんだ?と思って少し調べてみました。

ロケールをフランスに変換して実行すると

setlocale(LC_NUMERIC, 'fr_FR'); // ロケールをフランスに変換
var_dump(sprintf('%.5f', 0.00001)); // ロケールを考慮する
var_dump(sprintf('%.5F', 0.00001)); // ロケールを考慮しない
出力結果
string(7) "0,00001"
string(7) "0.00001"

なんと小数点の表記が違っていました!
どうやらフランスでは、小数点をカンマ(,)で表記するらしいです。

参考リンク

Discussion