PHPの小数精度のコード分析

不言
不言オリジナル
2018-08-04 14:02:412010ブラウズ

この記事の内容は、PHP の小数精度のコード解析に関するもので、一定の参考値がありますので、困っている方は参考にしていただければ幸いです。

プロジェクトで小数点第 2 位に四捨五入するときに精度の問題が発生しました:

$num = 0.99;
$num1 = round($num, 2);//0.98999999999999999
$num2 = floatval($num);//0.98999999999999999

現在の解決策:

sprintf("%.2f", round($money, 2));//会自动四舍五入
echo substr(sprintf("%.3f",$n), 0, -1);//不四舍五入

テスト結果:

var_dump(json_encode(round(0.99 ,2)));//0.98999999999999999
var_dump(round(0.99 ,2));//0.99

$f = 0.58;    
var_dump(intval($f * 100));//57

約 57問題を分析できます:

浮動小数点数の表現 (IEEE 754: IEEE 2 進浮動小数点演算標準):

64 ビット長 (倍精度) を単位とする浮動小数点数たとえば、 は、1 つの符号ビット (E)、11 の指数ビット (Q)、および 52 の仮数ビット (M) (合計 64 ビット) で表されます。

符号ビット: 最上位ビットは、の符号を示します。データ、0 は正の数を示し、1 は負の数を示します。

指数ビット: データの累乗を底 2 で表し、指数はオフセット コードで表されます。

仮数部: データの小数点以下の有効な桁を表します。

0.58 バイナリ表現の場合 たとえば、無限に長い値です:

0.58 のバイナリ表現は基本的に (52 ビット): 0010100011110101110000101000111101011100001010001111

0.57 のバイナリ表現 (52 ビット)基本的に:001000 111101011100001010001010101110000101000101010101010101010110

これら2つのビットでのみ計算されている場合、2つのバイナリ番号は次のとおりです。 ; 0.5699999999999999

したがって、0.58 * 100 の結果は次のようになります: 57.999999999、整数型に変換: 57

浮動小数点数のバイナリ表現については、次を参照してください。浮動小数点数

次と同様:

(0.1 + 0.7) == 0.8//false

floor((0.1+0.7)*10)//7 
//内部结果可能是:7.9999999999
//所以:不可能精确的用有限位数表达某些十进制分数
1/3=3.33333333333
//而3.333333333333333*3,却不是1

結論:

したがって、浮動小数点数の結果が最後の桁まで正確であるとは決して信じず、決して比較しないでください。 2 つの浮動小数点数が等しいかどうか。本当に高い精度が必要な場合は、任意精度の数学関数または gmp 関数を使用する必要があります。

高精度関数の使用をお勧めします:高精度関数

    bcadd — 2 つの任意精度数値の加算
  • bccomp — 2 つの任意精度の数値を比較する
  • bcp — 2 つの任意精度数値の除算を計算します

  • bcmod — 任意精度数値のモジュロ

  • bcmul — 2 つの任意精度数値の乗算計算
  • bcpow — 任意精度の数値のべき乗
  • bcpowmod — 任意の精度の数値を、指定された係数で減じた別の数値に上げます
  • bcscale — すべての bc 数学関数の小数点以下のデフォルト桁数を設定します
  • #bcsqrt — 任意精度数値の平方根

  • bcsub — 2 つの任意精度数値の減算
  • 高精度関数を使用して丸めを実現する:
  •     function getBcRound($number, $precision = 0)
        {
            $precision = ($precision < 0)
                       ? 0
                       : (int) $precision;
            if (strcmp(bcadd($number, &#39;0&#39;, $precision), bcadd($number, &#39;0&#39;, $precision+1)) == 0) {
                return bcadd($number, &#39;0&#39;, $precision);
            }
            if (getBcPresion($number) - $precision > 1) {
                $number = getBcRound($number, $precision + 1);
            }
            $t = &#39;0.&#39; . str_repeat(&#39;0&#39;, $precision) . &#39;5&#39;;
            return $number < 0
                   ? bcsub($number, $t, $precision)
                   : bcadd($number, $t, $precision);
        }
        
        function getBcPresion($number) {
            $dotPosition = strpos($number, &#39;.&#39;);
            if ($dotPosition === false) {
                return 0;
            }
            return strlen($number) - strpos($number, &#39;.&#39;) - 1;
        }
        
        $money = getBcRound(0.99, 2);

    おすすめ関連記事:

PHP での AES 暗号化ファイルの解析 (コード付き)

phpのcurlにおけるpostモードとgetモードのリクエストコード

PHP中間キーの内容解析について

以上がPHPの小数精度のコード分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。