formatFloat()
float を取り、それを 10 進展開文字列としてフォーマットする関数を作成したいと思います。例えば:
formatFloat(1.0E+25); // "10,000,000,000,000,000,000,000,000"
formatFloat(1.0E+24); // "1,000,000,000,000,000,000,000,000"
formatFloat(1.000001); // "1.000001"
formatFloat(1.000001E-10); // "0.0000000001000001"
formatFloat(1.000001E-11); // "0.00000000001000001"
初期のアイデア
float を文字列にキャストするだけではうまくいきません。なぜなら、 float が about よりも大きいか、 about1.0E+14
よりも小さい場合1.0E-4
、PHP はそれらを10 進数展開ではなく指数表記でレンダリングするからです。
number_format()
試してみる明らかな PHP 関数です。ただし、この問題は大きな float の場合に発生します。
number_format(1.0E+25); // "10,000,000,000,000,000,905,969,664"
number_format(1.0E+24); // "999,999,999,999,999,983,222,784"
小さい float の場合、難しいのは、要求する 10 進数の桁数を選択することです。1 つのアイデアは、多数の 10 進数を要求してからrtrim()
、余分な0
s を要求することです。0
ただし、10 進展開がsで終わらないことが多いため、この考えには欠陥があります。
number_format(1.000001, 30); // "1.000000999999999917733362053696"
number_format(1.000001E-10, 30); // "0.000000000100000099999999996746"
number_format(1.000001E-11, 30); // "0.000000000010000010000000000321"
問題は、浮動小数点数の精度が制限されており、通常、リテラルの正確な値を格納できないことです (例: 1.0E+25
)。代わりに、表現できる最も近い値を格納します。 number_format()
これらの「最も近い近似値」を明らかにしています。
Timo Frenay のソリューション
このコメントがページの奥深くに埋もれていることを発見しましたがsprintf()
、驚くべきことに賛成票はありませんでした。
大きさに関係なく、有効桁数が 16 桁の浮動小数点数を出力する方法を次に示します。
$result = sprintf(sprintf('%%.%dF', max(15 - floor(log10($value)), 0)), $value);
重要な部分は、 を使用して float の桁log10()
数を決定し、次に必要な 10 進数の桁数を計算することです。
修正が必要なバグがいくつかあります。
- このコードは、負の浮動小数点では機能しません。
- このコードは、非常に小さな float (例: ) では機能しません
1.0E-100
。PHP は次の通知を報告します: "sprintf()
: 要求された 116 桁の精度は、PHP の最大 53 桁に切り捨てられました" - である場合
$value
はです。0.0
log10($value)
-INF
- PHP floatの精度は「おおよそ 10 進数で 14 桁」なので、16 桁ではなく 14 桁を表示する必要があると思います。
私の最善の試み
これが私が思いついた最善の解決策です。これは Timo Frenay のソリューションに基づいており、バグを修正し、余分なsをトリミングするためにThiefMaster の正規表現を使用しています。0
function formatFloat($value)
{
if ($value == 0.0) return '0.0';
$decimalDigits = max(
13 - floor(log10(abs($value))),
0
);
$formatted = number_format($value, $decimalDigits);
// Trim excess 0's
$formatted = preg_replace('/(\.[0-9]+?)0*$/', '$1', $formatted);
return $formatted;
}
これは、200 個のランダムなフロートを使用した Ideone のデモです。コードは、約よりも小さいすべてのフロートに対して正しく機能するようです1.0E+15
。
number_format()
非常に小さなフロートでも正しく機能するのは興味深いことです。
formatFloat(1.000001E-250); // "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000001"
質問
私の最善の試みは、formatFloat()
まだこの問題に悩まされています:
formatFloat(1.0E+25); // "10,000,000,000,000,000,905,969,664"
formatFloat(1.0E+24); // "999,999,999,999,999,983,222,784"
この問題を解決するためにコードを改善するエレガントな方法はありますか?