2011-01-24 15 views
24

NUnitテストを実行して、既知のテストデータと計算結果を評価しています。数値は浮動小数点型の倍数であるため、それらは正確に等価であるとは思わないが、与えられた精度でそれらを等しく扱う方法がわからない。ある固定精度ではなく、2つの倍率が一定の精度で等しいかどうかを評価します。

我々は一定の許容範囲と比較することができNUnitのでは

double expected = 0.389842845321551d; 
double actual = 0.38984284532155145d; // really comes from a data import 
Expect(actual, EqualTo(expected).Within(0.000000000000001)); 

、それがゼロ以下の数字のために正常に動作しますが、数字は許容範囲が実際に変更する必要が育つように、我々は常に同じ数を気に精度の数字の

具体的には、このテストは失敗します。

double expected = 1.95346834136148d; 
double actual = 1.9534683413614817d; // really comes from a data import 
Expect(actual, EqualTo(expected).Within(0.000000000000001)); 

そしてもちろん、より大きな数字が許容範囲で失敗... 2つの浮動小数点数は、与えられた精度で等しい評価するための正しい方法を何

double expected = 1632.4587642911599d; 
double actual = 1632.4587642911633d; // really comes from a data import 
Expect(actual, EqualTo(expected).Within(0.000000000000001)); 

? NUnitでこれを行う組み込みの方法はありますか?

答えて

11

MSDNから:デフォルトで

17桁の最大が内部で管理されているが、ダブル値は、精度の15桁が含まれています。

ここで15としましょう。

したがって、公差を同じ程度にしたいと言うことができます。

小数点以下の桁数は正確ですか?小数点から最上位桁までの距離を知る必要がありますか?大きさこれをLog10で得ることができます。

次に、私たちが望む精度の値を得るために、1刻みで10桁の精度を除算する必要があります。

さて、あなたは私が持っているより多くのテストケースを実行する必要がありますが、これは動作するようです:

double expected = 1632.4587642911599d; 
    double actual = 1632.4587642911633d; // really comes from a data import 

    // Log10(100) = 2, so to get the manitude we add 1. 
    int magnitude = 1 + (expected == 0.0 ? -1 : Convert.ToInt32(Math.Floor(Math.Log10(expected)))); 
    int precision = 15 - magnitude ; 

    double tolerance = 1.0/Math.Pow(10, precision); 

    Assert.That(expected, Is.EqualTo(actual).Within(tolerance)); 

それは後半だ - ここに落とし穴があるかもしれません。私はあなたの3つのテストデータセットに対してそれをテストし、それぞれが合格しました。 pricision16 - magnitudeに変更すると、テストが失敗しました。許容値が大きかったので、それを14 - magnitudeに設定すると明らかに通過しました。

+0

これは非常に良い解決策ですが、 '0.0'を' 0.0'と比較すると例外が発生します。しかし、 '予想された'の周りの単純な 'if'条件がそれを処理します。 –

+0

ああ、そうです。私は何かがあると思った。 :)私はこれを考慮に入れて私の答えを更新しました。 – Brett

+0

これは本当にうまくいった。私は105単位テストを実行しました、正の半分と負の半分、そして全部で8を超えました。 Michael Borgwardtの答えは合格し、全く同じテストに合格しなかった。 –

5

各項目を文字列に変換して文字列を比較するのはどうですか?

string test1 = String.Format("{0:0.0##}", expected); 
string test2 = String.Format("{0:0.0##}", actual); 
Assert.AreEqual(test1, test2); 
+1

私はこれを考え、文字列に変換することに同意しました。あなたが正確に言ったように、あなたは、各値に対して利用可能な完全精度を使用して文字列に変換して、同じ長さで切り捨てたいと思うでしょう。しかし、私は数学的に適切な結果を望んでいます。少しハッキリです。 –

3

NUnitのでそれを行うには、組み込みの方法がある場合、私は知らないが、私は、あなたが探している10倍の精度により、各フロートを掛けるlong型として結果を保存し、比較することを示唆しています2人はお互いに恋し合います。たとえば

double expected = 1632.4587642911599d; 
double actual = 1632.4587642911633d; 
//for a precision of 4 
long lActual = (long) 10000 * actual; 
long lExpected = (long) 10000 * expected; 

if(lActual == lExpected) { // Do comparison 
    // Perform desired actions 
} 
+1

これは公差を使用することと数学的には同じですが、異なる方法で適用されています。 –

+0

しかしこれははるかに読みやすく簡単です –

0

これは、迅速なアイデアですが、方法については、彼らがゼロを下回るまで、それらをシフトダウン? num/(10^ceil(log10(num)))のようなものでなければなりません。 。 。それがうまくいくかどうかは分かりませんが、そのアイデアです。

1632.4587642911599/(10^ceil(log10(1632.4587642911599))) = 0.16324587642911599 
+0

これは少なくとも良いスタートのように見えます。この計算は正確にどのような影響を与えますか?精度の損失はありますか?明らかに、両方の比較可能な値の完全精度は気にしませんが、両方で精度が失われた場合、比較によって誤検出が生じる可能性があります。 –

8

これは私がThe Floating-Point Guideために思い付いた(Javaコードが、簡単に翻訳しなければならない、とあなたが本当に必要なテストスイートが付属しています)です:

public static boolean nearlyEqual(float a, float b, float epsilon) 
{ 
    final float absA = Math.abs(a); 
    final float absB = Math.abs(b); 
    final float diff = Math.abs(a - b); 

    if (a * b == 0) { // a or b or both are zero 
     // relative error is not meaningful here 
     return diff < (epsilon * epsilon); 
    } else { // use relative error 
     return diff/(absA + absB) < epsilon; 
    } 
} 

本当にトリッキーな質問比較する数値の1つがゼロのときに行うべきことです。最良の答えは、そのような比較は、普遍的であることを試みるのではなく、比較される数のドメインの意味を常に考慮しなければならないことである。

+0

@Michael Borgwardt、正確にイプシロンは何ですか? 0.0001の値は、実際には比較対象となりますか?テストを通して、私のテストデータのために働くイプシロンの適切な値を見つけることができます(期待されたときは真、期待されるときは偽)。しかし、私はこのパラメータに対して100%明確ではない。 –

+2

@ Samuel:εは相対誤差マージンであり、すなわち0.01のεは、2つの値の差が約1%未満でなければならないことを意味する。しかし、1つの値が0の場合、これは無意味になります。この場合、別の値をεの2乗より小さくする必要があります。それはかなり恣意的です。普遍的に有用な比較関数が存在しない可能性があるという結論を導く。 –

+0

@Michael Bordwardt、ありがとう、それは良い説明です。私は普遍的に有用な比較関数についてのあなたの声明を理解し、それに同意しますが、残念ながら、これは特定のエンタープライズアプリケーションではなく、ライブラリのためのものなので、可能な限り普遍的なものを探しています。私はまだいくつかのテストをしていますが、あなたのソリューションとBretts(もっとシンプルなソリューション)は同じケースで動作し、同じケースで失敗するようです。 –

0

方法について:

const double significantFigures = 10; 
Assert.AreEqual(Actual/Expected, 1.0, 1.0/Math.Pow(10, significantFigures)); 
+0

もちろん、期待値が0かそれに近い場合は、特殊なケースをコーディングする必要があります。ゼロには何桁の有効数字がありますか?ゼロか無限か? – Pughjl

関連する問題