PHPの浮動小数点計算問題

WBOY
リリース: 2016-06-23 13:30:18
オリジナル
1136 人が閲覧しました

最近、価格に関するプログラムを実行していたときに、非常に奇妙な問題に遭遇しました。問題が分かりました。次のコードを見てください:

$a = 86.1 - 80.0;
echo 6.1 - $a;
?>

echo の結果が 0 ではなく、5.3290705182E であることがわかります。 15.長い間考えましたが、理由は見つかりませんでした。 C言語で試してみましたが、結果は正常でした。同僚は以前にもこの問題に遭遇したことがあると言いましたが、おそらく PHP の実数値の範囲を超えていたと思われます。

解決策としては、PHP は型付けが弱い言語なので、上記のコードをエコーするときに a 変数に二重引用符を追加します。つまり、echo 6.1-"$a"; または echoround(6.1-$a);//ありがとうLittle Stone 子供靴の提供方法。

具体的な理由をご存知の方は、こちらにメッセージを残してください! :)

追記: 情報をくれた友人 s に感謝します (ここをクリック)。単純な小数は、浮動小数点数の精度を損なうことなく 2 進数形式に変換することはできません。は最後の桁まで正確ではないため、PHP では単純な浮動小数点数演算の精度が失われ、計算結果に混乱が生じる可能性があります。この記事での PHP 浮動小数点演算の結果は 5.3290705182E-15 であり、限りなく 0 に近づく数値です。この二人の友人と他の熱心な友人たちに感謝します! ^_^

PHP の +-*/ を使用して浮動小数点数を計算すると、不正確な計算結果によって問題が発生する可能性があります。たとえば、echo intval(0.58*100); は 58 ではなく 57 を出力します。実際、これはコンピューターの基礎となるバイナリが浮動小数点数を正確に表現できないというバグであり、私も Python を使用しているときにこの問題に遭遇しました。したがって、基本的にほとんどの言語は、正確な計算を行うためのクラス ライブラリまたは関数ライブラリを提供しています。たとえば、PHP には BC 高精度関数ライブラリがあります。以下では、PHP トレーニング教師のデーンが、よく使用されるいくつかの BC 高精度関数の使用法を紹介します。 。

 代码如下     <?php    $f = 0.58;    var_dump(intval($f * 100)); //为啥输出57?>
ログイン後にコピー

Why is the Output 57? Is it a bug in PHP?

バグについてはもちろん、同じような質問をする人がたくさんいるので、多くの学生がこのような疑問を抱いたことがあると思います。 ask on php.net...

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

浮動小数点数、長さ 64 ビット (倍精度) を例にします、 1 ビットの符号ビット (E)、11 ビットの指数ビット (Q)、52 ビットの仮数部 (M) を使用します (合計 64 ビット)

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

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

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

ここで重要なのは、その表現です。 2 進数での 10 進数の表現については、Baidu で検索できます。理解する必要がある重要な点は、2 進数表現では 0.58 が無限に長い値であるということです。次の数値は暗黙の 1)..

0.58 のバイナリ表現は基本的に (52 ビット): 00101000111101011100001010001111010111000010100011110 0.57 (52 ビット) のバイナリ表現は基本的に: 00100011 11010111000010100011110101 11000010100011110 そして、次の方法のみで計算した場合の 2 つの 2 進数これらの 52 ビットは次のとおりです: www.111cn.net

0.58 -> 0.579999999999999960.57 -> 0.5699999999999999 0.58 * 100 の具体的な浮動小数点乗算については、詳しくは説明しません。それ (浮動小数点) については、暗算で漠然と見ていきます... 0.58 * 100 = つまり、これは PHP のバグだとはもう思っていませんでしたが、これが実際のバグです...

PHP浮動小数点型の+-*%/に誤差があります

例:

1.

$a = 0.1;

$b = 0.7;

var_dump(($a + $b) = = 0.8);

出力される値は boolean false です

これはなぜでしょうか? PHP マニュアルには、浮動小数点数に関する次の警告メッセージがあります:

警告

浮動小数点精度

0.1 や 0.7 のような単純な小数はできないようです。精度を少しも損なうことなく内部バイナリ形式に変換できます。これにより、結果が混乱する可能性があります。たとえば、floor((0.1+0.7)*10) は、結果の内部表現が 7.9999999999… のようなものになるため、通常、期待される 8 ではなく 7 を返します。

これは、限られた桁数で特定の小数を正確に表現することが不可能であるという事実に関連しています。たとえば、10 進数の 1/3 は 0.3333333 になります。

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

<?php$a = 0.1;$b = 0.7;var_dump(bcadd($a,$b,2) == 0.8);
ログイン後にコピー

bcadd ? 2 つの高精度の数値を加算します

bccomp ? 2 つの高精度の数値を比較し、-1、0、1 を返します

bcdiv ? 2 つの高精度数値を除算します

  bcmod ? 求高精度数字余数

  bcmul ? 将两个高精度数字相乘

  bcpow ? 求高精度数字乘方

  bcpowmod ? 求高精度数字乘方求模,数论里非常常用

  bcscale ? 配置默认小数点位数,相当于就是Linux bc中的”scale=”

  bcsqrt ? 求高精度数字平方根

  bcsub ? 将两个高精度数字相减

  整理了一些实例

  php BC高精确度函数库包含了:相加,比较,相除,相减,求余,相乘,n次方,配置默认小数点数目,求平方。这些函数在涉及到有关金钱计算时比较有用,比如电商的价格计算。

/**  * 两个高精度数比较  *   * @access global  * @param float $left  * @param float $right  * @param int $scale 精确到的小数点位数  *   * @return int $left==$right 返回 0 | $left<$right 返回 -1 | $left>$right 返回 1  */var_dump(bccomp($left=4.45, $right=5.54, 2));// -1   /**  * 两个高精度数相加  *   * @access global  * @param float $left  * @param float $right  * @param int $scale 精确到的小数点位数  *   * @return string   */var_dump(bcadd($left=1.0321456, $right=0.0243456, 2));//1.04   /**  * 两个高精度数相减  *   * @access global  * @param float $left  * @param float $right  * @param int $scale 精确到的小数点位数  *   * @return string   */var_dump(bcsub($left=1.0321456, $right=3.0123456, 2));//-1.98   /**  * 两个高精度数相除  *   * @access global  * @param float $left  * @param float $right  * @param int $scale 精确到的小数点位数  *   * @return string   */var_dump(bcdiv($left=6, $right=5, 2));//1.20  /**  * 两个高精度数相乘  *   * @access global  * @param float $left  * @param float $right  * @param int $scale 精确到的小数点位数  *   * @return string   */var_dump(bcmul($left=3.1415926, $right=2.4569874566, 2));//7.71  /**  * 设置bc函数的小数点位数  *   * @access global  * @param int $scale 精确到的小数点位数  *   * @return void   */ bcscale(3);var_dump(bcdiv('105', '6.55957')); // 16.007
ログイン後にコピー

注意点:关于设置的位数,超出部分是丢弃掉,而不是四舍五入。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート