ホームページ  >  記事  >  バックエンド開発  >  PHP 浮動小数点精度に関する質問

PHP 浮動小数点精度に関する質問

怪我咯
怪我咯オリジナル
2017-07-11 13:56:103287ブラウズ

C言語やC#言語では、浮動小数点型のデータを単精度型(float)と倍精度型(double)を使って格納しており、Floatデータは32ビット、doubleデータは64ビットを占有します。変数 float f= 2.25f の場合、メモリはどのように割り当てられますか?ランダムに割り当てられたら世界は大混乱になるのではないでしょうか? 実際、記憶方式に関しては、float と double はどちらも IEEE の仕様に準拠しており、double は R64.53 に準拠しています。

単精度であろうと倍精度であろうと、ストレージは 3 つの部分に分割されます:

符号ビット (Sign): 0 は正を表し、1 は負を表します

指数ビット (Exponent): 科学的表記法の指数データを格納するために使用されます。シフトストレージを使用します

Mantissa (Mantissa): 仮数部

この記事では、主に PHP 浮動小数点数の精度の問題について概要を紹介します。この記事では、3 つの異なる段落を使用して、PHP 浮動小数点数の精度損失の問題に焦点を当てます。この問題の原因と解決策は、困っている友人が参考にしてください

1. PHP 浮動小数点精度損失問題

まず、次のコードを見てください:


コードは次のとおりです:

$f = 0.57;
echo intval($f * 100);  //56

少し驚くかもしれませんが、PHP は IEEE 754 倍精度に従っています:

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

符号ビット: 最上位ビットはデータの符号を示し、0 は正の数を示し、1 は負の数を示します。
指数ビット:データの累乗を基数2で表し、指数はオフセットコードで表されます。
仮数:データの小数点以下の有効桁を表します。

小数がどのように表現されるかを見てみましょう。二進数で:

2を掛けて、切り上げて、順番に並べます。つまり、小数部を2倍してから

整数部を取り、引き続き残りの小数部に2を掛けてから、整数部分を取ります, そして、残った小数部にもう一度2をかけて、小数部がとれるまで掛けますが、0.57のようになります。 このように小数を掛け続けると、小数部は0にはなりません。 有効桁の小数点は、2進数で表現すると無限大になります。

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

52 ビットしかない場合、0.57 => 0.5699999999999 99995

上記の予期せぬ結果を確認するのは難しくありません。

2. PHP 浮動小数点数の精度の問題

まずは質問を見てみましょう:

コードは次のとおりです:

$f = 0.58;
var_dump(intval($f * 100)); //为啥输出57

このような疑問を持ったことがある学生は多いと思います。

具体的な原理については、「Brother Bird」の記事で詳しく説明されています: PHP 浮動小数点数の

FAQ への回答

それでは、この問題を回避するにはどうすればよいでしょうか?

多くの方法がありますが、ここでは 2 つ説明します:
1. sprintf

コードは次のとおりです:

substr(sprintf("%.10f", ($a/ $b)), 0, -7);

2.round (四捨五入されることに注意してください)


コードは次のとおりです:

round($a/$b, 3);

または、より良い方法がある場合は、メッセージを残して教えてください。

3. PHP 浮動小数点数に関するよくある質問への答え

PHP 浮動小数点数については、以前に次の記事を書きました: PHP の浮動小数点数についてはすべて「偽物」です

ただし、その部分で 1 つ見逃していました。これは、次の一般的な質問に対する答えです:


コードは次のとおりです:

<?php
    $f = 0.58;
    var_dump(intval($f * 100)); //为啥输出57
?>

なぜ出力は 57 なのでしょうか? PHP のバグですか?

このような質問をしたことがある学生は多いと思います。なぜなら、bugs.php.net でよく質問する人は言うまでもなく、同様の質問をする人がたくさんいるからです...

この理由を理解するには、まず浮動小数点数 (IEEE 754) の表現を知る必要があります:

浮動小数点数は、長さが 64 ビット (倍精度) の場合、1 つの符号ビット (E)、11 の指数ビット (Q)、および 52 の仮数ビット (M) (合計 64 ビット) で表されます。 ).

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

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

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

ここでの重要な点は、 2 進数での 10 進数の表現についてはどうですか? 2 進数で表現すると、Baidu で検索できます。ここでは詳しく説明しませんが、理解する必要があるのは、2 進数表現の場合、0.58 は無限に長い値であるということです。次の数値は暗黙の 1) を省略します。..

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

そして計算すると、この 2 つの 2 進数これらの 52 ビットのみを介して、次のことが可能です:




コードをコピーします

コードは次のとおりです:

0.58 -> 0.57999999999999996
0.57 -> 0.56999999999999995
0.58 * 100 の具体的な浮動小数点乗算については、詳しくは考慮しません。 (浮動小数点)を見て、暗算で漠然と見てみると… 0.58 * 100 = 57.999999999

それを積分すると、当然57になります…

この問題の重要なポイントは、「一見有限に見える 10 進数は、コンピューターの 2 進数表現では実際には無限である」ということです。

以上がPHP 浮動小数点精度に関する質問の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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