2012-01-09 7 views
49

なぜこのコードは.NET 4にFalseを出力しますか?明示的なキャストによって予期しない動作が発生しているようです。浮動小数点型の結果を返すメソッドで結果をキャストすると結果が変化する

「浮動小数点は不正確です」または「しないでください」を超える回答が必要です。

float a(float x, float y) 
{ 
    return (x * y); 
} 

float b(float x, float y) 
{ 
    return (float)(x * y); 
} 

void Main() 
{ 
    Console.WriteLine(a(10f, 1f/10f) == b(10f, 1f/10f)); 
} 

PS:このコードはリリースコードではなくユニットテストから来たものです。コードは意図的にこのように書かれました。私はそれが最終的に失敗するだろうと思ったが、正確かつ正確な理由を正確に知りたかった。答えは、浮動小数点決定論の通常の理解を超えた理解を提供するため、この技法の妥当性を証明します。そして、それがこのコードをこのように書いた点です。意図的な探査。

PPS:ユニットテストは、.NET 3.5で渡しましたが、現在は.NET 4.

+9

浮動小数点変数は、定義上、100%正確ではありません。彼らはおおよその数字です。 http://msdn.microsoft.com/en-us/library/b1e65aza.aspxこの記事も参照してください。http://stackoverflow.com/questions/618535/what-is-the-difference-between-decimal-float -and-double-in-c ランタイムの*いずれの*風味でも同じであるという保証はありません。 – David

+0

さらに役に立つリンク:http://msdn.microsoft.com/en-us/library/ms187912.aspx – David

+4

それにもかかわらず、興味深い質問です。機能的には、浮動小数点近似を使用しても、これらのメソッドAとBが同じ操作を実行することが期待されます。私は説明を聞くことに興味がある。 –

答えて

76

Davidのコメントは正しいが、不十分強いへのアップグレード後に失敗しました。同じプログラムでという計算を2回実行する保証はありません同じ結果が得られます。

C#の仕様はこの点で非常に明確である:


浮動小数点演算は、演算の結果の型よりも高精度に行うことができます。たとえば、一部のハードウェアアーキテクチャでは、double型よりも範囲と精度の高い「拡張型」または「long double型」浮動小数点型をサポートし、この高精度型を使用してすべての浮動小数点演算を暗黙的に実行します。このようなハードウェアアーキテクチャでは、パフォーマンスが過度に高くても精度が低い浮動小数点演算を実行でき、パフォーマンスと精度の両方を失うために実装する必要はなく、すべての浮動小数点演算でより高い精度の型を使用できます。より正確な結果を出す以外に、測定可能な影響はほとんどありません。ただし、乗算によって倍精度の範囲外の結果が生成されますが、後続の除算で一時的な結果が倍精度の範囲に戻される形式の式のx * y/zでは、式が高い範囲の形式で評価されることにより、無限大ではなく有限の結果が生成される。


C#コンパイラは、ジッタおよびランタイムのすべては気まぐれで、あなたにいつでも、仕様によって必要とされるよりもをより正確な結果を与えるために、幅広いlattitudeを持っている - 彼らは必要とされていません一貫してそうすることを選択することは実際にはありません。

あなたが好きではない場合は、2進浮動小数点数を使用しないでください。小数点以下の桁数または任意の精度小数点を使用します。 floatを返すメソッドに浮かぶようにキャスティング理由を私は理解していない

は、それが

優秀なポイントを行い違いになります。

サンプルプログラムは、小さな変更が大きな影響をどのように引き起こす可能性があるかを示しています。ランタイムのあるバージョンでは、明示的にfloatにキャストすると明示的に異なる結果が返されることに注意してください。明示的にfloatにキャストすると、C#コンパイラはランタイムに「この最適化を使用する場合、この精度を高精度モードから外してください」というヒントを示します。仕様書には、が記載されているように、これには潜在的なパフォーマンスコストがあります。

"正解"に丸まったことは単なる幸せな事故です。この場合にはの精度が失われて正しい方向に失われたため、正しい答えが得られます

.net 4とはどのように違いますか?

3.5と4.0のランタイムの違いは何ですか?その違いは、4.0では、ジッタが特定のケースでより高い精度に向かうことを選択し、3.5ジッタが選択しないことです。それは、この状況が3.5では不可能であったことを意味するものではありません。ランタイムのすべてのバージョンとC#コンパイラのすべてのバージョンで可能でした。マシン上で詳細が異なる場合があります。しかし、ジッタはであり、常にがこの最適化を行うことを許可されています。

C#コンパイラは、コンパイル時に定数浮動小数点数を計算するときに同様の最適化を行うことができます。コンパイラの実行時状態の詳細に応じて、定数の2つの一見同一の計算が異なる結果になることがあります。

さらに一般的には、浮動小数点数の実数の代数的性質が現実と完全に一致していないと予想されます。これらの代数的性質はありません。浮動小数点演算は、アソシエートでもありません;あなたが期待しているように、彼らは乗法的逆の法則に従うことは確かではありません。浮動小数点数は実際の算術の近似値に過ぎません。たとえば、物理システムをシミュレートしたり、要約統計量を計算したり、そのようなことを行うのに十分な近似値です。

+6

残念ながら私は浮動小数点が正確ではないことを知っていました。私が知りませんでしたのは、キャスティングと最適化に関する他のすべてでした。私が答えを知っていれば、正しい質問をする方が簡単でしょう。 –

+2

私は、浮動小数点数に「不正確」という言葉が適用されるのが好きではありません。 '(float)1'は100%正確です。小さな整数と2の累乗による乗算を通常の結果で追加することも100%正確です。この例の違いは、 '不正確な'計算の結果ではなく、(エリックが長らく説明したように)特定の実装の詳細を選択する言語とランタイムの自由の結果です。 –

+1

@JeffreySax:あなたがそれを呼びたいものは何でもいいです。そして、それは多くの人が間違って、または不完全にこの問題を原因としていることです。問題のポイントは「他のもの」に到達することでした。それが起こるためには、質問と回答の両方を編集する必要がありました。浮動小数点 '精度'の基礎については誰も言及していないことを願っています。そのため、浮動小数点の精度に関するコメントだけが依然として浮上しています。 –

0

私は今Microsoftコンパイラを持っていませんし、Monoもそのような影響はありません。 私が知る限りGCC 4.3+ uses gmp and mpfr to calculate some stuff in compile time。 C#コンパイラは、同じアセンブリ内の非仮想メソッド、静的メソッド、またはプライベートメソッドに対しても同じ処理を実行できます。明示的なキャストは、そのような最適化を妨げる可能性があります(ただし、同じ動作をすることはできません)。私。それは一定の表現を何らかのレベル(例えば、キャストまでのb()の場合)の計算とインラインにすることができます。

GCCも、それが理にかなっていると、操作をより高精度に促進する最適化機能を備えています。

私は両方の最適化を潜在的な理由として考えています。しかし、両方とも、結果の明示的なキャストに「標準に近づく」のようないくつかの追加の意味がある理由はありません。

関連する問題