12

(ほとんどの)浮動小数点数が正確に格納されないことは一種の一般的な知識です(IEEE-754形式が使用されている場合)。したがって、これを行うべきではありません。

0.3 - 0.2 === 0.1; // very wrong

...代わりにfalse、特定の任意精度の型/クラス(Java / RubyのBigDecimal 、PHPのBCMath、PerlのMath :: BigInt / Math :: BigFloatなど)を使用ない限り、結果として得られます。

しかし、なぜこの式の結果を出力しようとすると0.3 - 0.2、スクリプト言語(PerlPHP)は与える0.1のに、「仮想マシン」(JavaJavaScriptErlang0.09999999999999998 )は代わりにもっと似たものを与えるのだろうか?

また、Rubyでも一貫性がないのはなぜですか?バージョン1.8.6(コードパッド)は提供0.1し、バージョン1.9.3(ideone)は提供します0.0999...

4

5 に答える 5

7

PHPに関しては、出力は精度のini設定に関連しています:

ini_set('precision', 15);
print 0.3 - 0.2; // 0.1

ini_set('precision', 17);
print 0.3 - 0.2; //0.099999999999999978 

これは、他の言語の原因でもある可能性があります

于 2012-12-29T14:16:40.720 に答える
4

浮動小数点数は異なる目的で印刷されるため、異なる方法で印刷されます。

浮動小数点数の出力は変換操作です。内部形式でエンコードされた値は 10 進数に変換されます。ただし、変換の詳細については選択肢があります。

(A)正確な数学を行っていて、内部形式で表される実際の値を確認したい場合、変換は正確でなければなりません: 入力とまったく同じ値を持つ 10 進数を生成する必要があります。(各浮動小数点数は正確に 1 つの数値を表します。IEEE 754 標準で定義されているように、浮動小数点数は間隔を表していません。) 場合によっては、非常に多くの桁数を生成する必要がある場合があります。

(B)正確な値は必要ないが、内部形式と 10 進数を相互に変換する必要がある場合は、他の結果と区別できるように正確に (そして正確に) 10 進数に変換する必要があります。つまり、内部形式で隣接する数値を変換して得られる結果とは異なる結果になるように、十分な桁数を生成する必要があります。これには多数の数字を生成する必要があるかもしれませんが、管理できないほど多くはありません。

(C)読者に数の感覚を与えたいだけで、アプリケーションが必要に応じて機能するために正確な値を生成する必要がない場合は、必要な数の数字のみを生成する必要があります。特定のアプリケーション。

変換を行うには、次のうちどれを行う必要がありますか?

言語が異なれば、異なる目的で開発されたため、または開発中に正確な結果を生成するために必要なすべての作業を行うのが適切ではなかったため、またはその他のさまざまな理由で、異なるデフォルトがあります。

(A) 注意深いコードが必要であり、一部の言語またはその実装では、この動作が提供されていないか、提供されることが保証されていません。

(B) は Java で必須だと思います。ただし、最近の質問で見たように、予期しない動作が発生する可能性があります。(65.12後者には近くの値と区別するのに十分な桁数があるため、65.12-2「65.12」と出力されますが、それと 63.12 の間に別の浮動小数点値があるため、「63.120000000000005」と出力されます。 )

(C) は、一部の言語がデフォルトで使用するものです。印刷する桁数の単一の値がすべてのアプリケーションに適しているわけではないため、これは本質的に間違っています。実際、関係する真の値を隠すことによって、浮動小数点数に関する継続的な誤解を助長することを何十年にもわたって見てきました。ただし、実装が簡単なため、一部の実装者にとっては魅力的です。理想的には、言語はデフォルトで浮動小数点数の正しい値を出力する必要があります。より少ない桁数を表示する場合、希望する結果を生成するための適切な桁数を考慮して、アプリケーションの実装者のみが桁数を選択する必要があります。

さらに悪いことに、一部の言語では、実際の値またはそれを区別するのに十分な桁数を表示しないことに加えて、生成された桁数が何らかの意味で正しいことを保証しません (正確な値を数値に丸めて得られる値であるなど)。示されている数字の数)。この動作に関する保証を提供しない実装でプログラミングする場合、エンジニアリングを行っているわけではありません。

于 2012-12-29T17:21:52.643 に答える
2

このプロパティが必要な場合

  • 2 つの異なる float ごとに、異なる印刷表現があります

または、REPL に役立つさらに強力なもの

  • 印刷された表現は、変更されずに再解釈されなければならない

次に、基数 2 の内部表現を持つ float/double を基数 10 に出力するための 3 つのソリューションが表示されます。

  1. 正確な表現を印刷します。
  2. 十分な 10 進数を出力する (適切な丸めを使用)
  3. 変更せずに再解釈できる最短の 10 進数表現を出力します

基数 2 では浮動小数点数は an_integer * 2^an_exponent であるため、その基数 10 の正確な表現の桁数は有限です。
残念ながら、これは非常に長い文字列になる可能性があります...たとえば、1.0e-10 は 1.0000000000000000364321973154977415791655470655996396089904010295867919921875e-10 と正確に表されます

解決策 2 は簡単です。IEEE-754 double に 17 桁の printf を使用します...
欠点: 正確でも最短でもありません! 0.1 を入力すると、0.100000000000000006 になります。

解決策 3 は、REPL 言語に最適な方法です。0.1 を入力すると、0.1 が出力
されます。残念ながら、標準ライブラリにはありません (残念です)。
少なくとも、Scheme、Python、そして最近の Squeak/Pharo Smalltalk は正しく機能しています。Java もそうだと思います。

于 2012-12-29T15:27:40.413 に答える
2

PHP は自動的に数値を任意の精度に丸めます。

一般に、浮動小数点数は正確ではありません (ご指摘のとおり) round()。少数の小数点以下の桁数だけで比較する必要がある場合は、言語固有の関数を使用する必要があります。それ以外の場合は、方程式の絶対値を取得し、それらが特定の範囲内にあるかどうかをテストします。

php.netからの PHP の例:

$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;
if(abs($a - $b) < $epsilon) {
  echo "true";
}

Ruby の問題に関しては、異なるバージョンを使用しているようです。Codepad は を使用しますが1.8.6、Ideaone は を使用します1.9.3が、どこかの構成に関連している可能性が高くなります。

于 2012-12-29T14:17:21.953 に答える
0

Javascriptに関しては、base2が内部で計算に使用されています。

> 0.2 + 0.4
0.6000000000000001

そのため、結果の base2 数値が周期的でない場合、Javascript は偶数のみを配信できます。

0.6 は0.10011 10011 10011 10011 ...base2 (定期的) ですが、0.5そうではないため正しく出力されます。

于 2012-12-29T15:23:31.573 に答える