2010-08-06 58 views
16

我正在編寫驗證數據庫中的計算的單元測試,並且有很多四捨五入和截斷以及意味着有時數字稍微偏離的東西。如何找到兩個變量是否近似相等?

驗證時,我發現很多時候事情會通過,但說他們失敗 - 例如,這一數字將在1和我得到0.999999

我的意思是,我可能只是一輪一切爲整數,但因爲我使用了很多隨機的樣本,最終我要去得到這樣的

10.5 10.4999999999

人會舍入到10,其他將輪11 。

我應該如何解決這個問題,我需要的東西大致正確?

+1

比較浮點數,2012版:https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ - 不是C#版本,你會得到你所有的東西需要 – 2016-09-30 12:40:04

回答

35

定義一個公差值(也就是一個「小量」),例如,0.00001,然後用比較像這樣的區別:

if (Math.Abs(a - b) < epsilon) 
{ 
    // Values are within specified tolerance of each other.... 
} 

[你可以使用Double.Epsilon但你必須使用乘以因子。]

更好的是,寫一個擴展方法來做同樣的事情。我們在單元測試中有類似Assert.AreSimiliar(a,b)的東西。

+8

Microsoft提供了一個提供增量的Assert.AreEqual類的重載。 '公共靜態無效AreEqual(double expected,double actual,double delta);'看起來它已經存在,因爲至少Visual Studio 2005 https://msdn.microsoft.com/en-us/library/ms243458(v=vs。 80).aspx – user2023861 2016-05-06 16:06:52

+0

NUnit還爲其Assert.AreEqual方法提供了重載,以允許提供增量。 – BrettMStory 2017-02-14 23:04:51

14

您可以提供包含兩個值之間可接受差異的參數的函數。例如

// close is good for horseshoes, hand grenades, nuclear weapons, and doubles 
static bool CloseEnoughForMe(double value1, double value2, double acceptableDifference) 
{ 
    return Math.Abs(value1 - value2) <= acceptableDifference; 
} 

然後調用它

double value1 = 24.5; 
double value2 = 24.4999; 

bool equalValues = CloseEnoughForMe(value1, value2, 0.001); 

如果你想稍微專業的話,你可以調用函數ApproximatelyEquals或類似的規定。

static bool ApproximatelyEquals(this double value1, double value2, double acceptableDifference) 
+5

+1爲正確答案,+1爲推薦擴展名,-1爲不足以使用希臘字母。 :-) – 2010-08-06 03:47:04

+8

我會稱之爲'CloseEnoughForGovernmentWork()',親自 – kyoryu 2010-08-06 03:50:55

3

比較浮點數的一種方法是比較將它們分開的浮點表示的數量。這個解決方案對數字的大小無關緊要,因此您不必擔心其他答案中提到的「epsilon」的大小。

該算法的說明可以找到here(最後的AlmostEqual2sComplement函數),這裏是我的C#版本。

更新: 提供的鏈接已過時。新版本,其中包括一些改進和錯誤修正爲here

public static class DoubleComparerExtensions 
{ 
    public static bool AlmostEquals(this double left, double right, long representationTolerance) 
    { 
     long leftAsBits = left.ToBits2Complement(); 
     long rightAsBits = right.ToBits2Complement(); 
     long floatingPointRepresentationsDiff = Math.Abs(leftAsBits - rightAsBits); 
     return (floatingPointRepresentationsDiff <= representationTolerance); 
    } 

    private static unsafe long ToBits2Complement(this double value) 
    { 
     double* valueAsDoublePtr = &value; 
     long* valueAsLongPtr = (long*)valueAsDoublePtr; 
     long valueAsLong = *valueAsLongPtr; 
     return valueAsLong < 0 
      ? (long)(0x8000000000000000 - (ulong)valueAsLong) 
      : valueAsLong; 
    } 
} 

如果你想比較浮點數,改變所有doublefloat,所有longint0x80000000000000000x80000000

使用representationTolerance參數,您可以指定容許錯誤的大小。數值越高意味着接受的誤差越大。我通常使用值10作爲默認值。

+0

比較零和非常小的負值如何?似乎它不起作用。例如比較0和-1.1102230246251565E-16 – 2016-09-30 10:16:26

+0

提供的鏈接已過時。新的位置是https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ – 2016-09-30 12:36:37

+0

我不明白你爲什麼從0x800減去負值...可能你請詳細說明這一點? – m93a 2017-12-26 20:24:49

8

我還沒有檢查添加了哪個MS Test版本,但是在v10.0.0.0中,Assert.AreEqual方法有重載接受delta參數的內容並進行近似比較。

I.e.

// 
// Summary: 
//  Verifies that two specified doubles are equal, or within the specified accuracy 
//  of each other. The assertion fails if they are not within the specified accuracy 
//  of each other. 
// 
// Parameters: 
// expected: 
//  The first double to compare. This is the double the unit test expects. 
// 
// actual: 
//  The second double to compare. This is the double the unit test produced. 
// 
// delta: 
//  The required accuracy. The assertion will fail only if expected is different 
//  from actual by more than delta. 
// 
// Exceptions: 
// Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException: 
//  expected is different from actual by more than delta. 
public static void AreEqual(double expected, double actual, double delta); 
1

問題被問如何斷言某事是在單元測試中幾乎相等。通過使用內置的Assert.AreEqual函數,您聲明的內容幾乎相同。例如:

Assert.AreEqual(expected: 3.5, actual : 3.4999999, delta:0.1);

此測試將通過。問題解決了,無需編寫自己的功能!