11

LinuxサーバーでPHP5.2.13を使用しています。数値を四捨五入すると、奇妙なエラーが発生します。これは私のテストケースです:

<?php
echo "        " . round(1.505, 2) . "\n";
echo "       " . round(11.505, 2) . "\n";
echo "      " . round(111.505, 2) . "\n";
echo "     " . round(1111.505, 2) . "\n";
echo "    " . round(11111.505, 2) . "\n";
echo "   " . round(111111.505, 2) . "\n";
echo "  " . round(1111111.505, 2) . "\n";
echo " " . round(11111111.505, 2) . "\n";
echo "" . round(111111111.505, 2) . "\n";

これは結果です:

        1.51
       11.51
      111.51
     1111.51
    11111.51
   111111.51
  1111111.5
 11111111.51
111111111.51

誰もがこれを引き起こす原因を知っていますか?PHPは共有サーバーであるため、更新できません。

4

6 に答える 6

12

これは、数値1111111.505を浮動小数点表記で正確に表すことができないためです。それが得ることができる最も近いものは1111111.5049999999です。つまり、最終的に発生するのは、コード内の数値を1111111.50499999999に変換してから、丸めを行うことです。その結果、1111111.5になります。浮動小数点数には、一見単純な10進数でさえ完全な精度で表現できないという問題があります。例えば。数値0.1は、2進浮動小数点を使用して正確に表すことはできません。Pythonを使用して、0.1と入力すると、0.10000000000001が返されます。プラスマイナスいくつかのゼロ。これが、.Netなどの一部の言語が、特定の範囲と小数点以下の桁数内のすべての小数点以下の値を表すことができる「小数点以下の桁数」データ型を提供する理由です。

于 2011-02-07T13:51:04.780 に答える
8

それでも誰かがこのページに到達した場合、浮動小数点の減算によってエラーまたは奇妙な値が発生するという同様の問題が発生します。この問題についてもう少し詳しく説明したいと思います。原因は浮動小数点数です。問題を説明するために、簡単な例でその理由を説明し、そこから、なぜそれが丸めなどに影響を与えるのかを推測できます。

PHPとは直接関係がなく、バグでもありません。ただし、すべてのプログラマーはこの問題に注意する必要があります。

この問題は、20年前にも多くの命を奪いました。

1991年2月25日、MIM-104パトリオットミサイルバッテリーの浮動数計算におけるこの問題により、サウジアラビアのダーランで入ってくるスカッドミサイルを迎撃できなくなり、米軍の第14クォーターマスターデタッチメントから28人の兵士が死亡しました。

しかし、なぜそれが起こるのでしょうか?

その理由は、浮動小数点値は限られた精度を表すためです。そのため、処理後の値の文字列表現が同じにならない場合があります。また、スクリプトに浮動小数点値を記述し、数学演算なしで直接印刷することも含まれます。

簡単な例:

$a = '36';
$b = '-35.99';
echo ($a + $b);

あなたはそれが0.01を印刷することを期待するでしょう?しかし、0.009999999999998のような非常に奇妙な答えが出力されます

他の数値と同様に、浮動小数点数doubleまたはfloatは、0と1の文字列としてメモリに格納されます。浮動小数点が整数とどのように異なるかは、0と1を調べたいときにそれらをどのように解釈するかという点にあります。それらがどのように保存されるかについては多くの基準があります。

浮動小数点数は通常、左から右に、符号ビット、指数フィールド、および仮数または仮数としてコンピューターデータにパックされます。

十分なスペースがないため、10進数は2進数で適切に表されません。だから、uouは0.3333333のように1/3を正確に表現することはできません...そうですか?0.01を2進浮動小数点数として表現できないのも同じ理由です。1/100は0.00000010100011110101110000.....で、10100011110101110000が繰り返されます。

0.01を簡略化してシステムで切り捨てた形式の01000111101011100001010を2進数で保持すると、10進数に変換すると、0.0099999のように読み取られます。システムによっては...(64ビットコンピューターの方が32ビットよりもはるかに高い精度が得られます) )。この場合、オペレーティングシステムは、表示どおりに印刷するか、より人間が読める形式で印刷するかを決定します。したがって、それをどのように表現したいかはマシンに依存します。ただし、さまざまな方法で言語レベルで保護できます。

結果をフォーマットする場合は、echo number_format(0.009999999999998、2); 0.01を出力します。

これは、この場合、どのように読み取るか、どの程度の精度が必要かを指示するためです。参照1、2、3、4、5 _ _ _ _ _ _ _

于 2014-12-18T04:42:16.663 に答える
3

PHPバージョンではなく、wetwaerをアップグレードする必要があります。

エコー " " 。ラウンド(1.505、2)。"\ n";

PHPに1.505の丸められた値を返すように要求しているように見えますが、実際に、1.505のバイナリバージョンの丸められたバージョンの10進数バージョンを返す必要があります。

ここに数値を入力して、バイナリ表現を確認してください。

于 2011-02-07T13:20:11.817 に答える
3

フロートを使用することによって引き起こされる精度の問題に遭遇しているように私には思えます。フロートが完全に正確であるとは限りません。この問題は、あるシステムでは発生しますが、別のシステムでは発生しない場合があります。

任意の浮動小数点数の精度が本当に気になる場合は、bcmathまたはgmpが利用可能な場合はそれを使用しますが、bcmathを使用する場合は、bcround()関数を作成する必要があります。私が実際に機能するものは、php.netbcscaleページのコメントに投稿されています。

function bcround($number, $scale=0) {
        $fix = "5";
        for ($i=0;$i<$scale;$i++) $fix="0$fix";
        $number = bcadd($number, "0.$fix", $scale+1);
        return    bcdiv($number, "1.0",    $scale);
}
于 2011-02-07T13:24:45.970 に答える
2

@Shabbyrobe:関数が間違っています:この数値を2のスケールで丸めてみてください:44069.3445

44069.35である必要がありますが、デフォルトのphp round()-および関数は44069.34を返します

php.netのメンバーによる作業コード:

function mround($number, $precision=0) {

$precision = ($precision == 0 ? 1 : $precision);   
$pow = pow(10, $precision);

$ceil = ceil($number * $pow)/$pow;
$floor = floor($number * $pow)/$pow;

$pow = pow(10, $precision+1);

$diffCeil     = $pow*($ceil-$number);
$diffFloor     = $pow*($number-$floor)+($number < 0 ? -1 : 1);

if($diffCeil >= $diffFloor) return $floor;
else return $ceil;
}
于 2013-10-08T08:59:25.890 に答える
1

Shabbyrobeの回答のコードは、負の数を処理しません。これはコードの実用的な改善であり、負の数とデフォルトのスケールを処理します。

function bcround($number, $scale = null) {
    if (is_null($scale)) {
        $scale = bcscale();
    }
    $fix = ($number < 0) ? '-' : '';
    $fix .= '0.' . str_repeat('0', $scale) . '5';
    $number = bcadd($number, $fix, $scale + 1);
    return bcdiv($number, "1.0", $scale);
}
于 2020-07-03T19:13:29.000 に答える