我正在編寫驗證數據庫中的計算的單元測試,並且有很多四捨五入和截斷以及意味着有時數字稍微偏離的東西。如何找到兩個變量是否近似相等?
驗證時,我發現很多時候事情會通過,但說他們失敗 - 例如,這一數字將在1和我得到0.999999
我的意思是,我可能只是一輪一切爲整數,但因爲我使用了很多隨機的樣本,最終我要去得到這樣的
10.5 10.4999999999
人會舍入到10,其他將輪11 。
我應該如何解決這個問題,我需要的東西大致正確?
我正在編寫驗證數據庫中的計算的單元測試,並且有很多四捨五入和截斷以及意味着有時數字稍微偏離的東西。如何找到兩個變量是否近似相等?
驗證時,我發現很多時候事情會通過,但說他們失敗 - 例如,這一數字將在1和我得到0.999999
我的意思是,我可能只是一輪一切爲整數,但因爲我使用了很多隨機的樣本,最終我要去得到這樣的
10.5 10.4999999999
人會舍入到10,其他將輪11 。
我應該如何解決這個問題,我需要的東西大致正確?
定義一個公差值(也就是一個「小量」),例如,0.00001,然後用比較像這樣的區別:
if (Math.Abs(a - b) < epsilon)
{
// Values are within specified tolerance of each other....
}
[你可以使用Double.Epsilon
但你必須使用乘以因子。]
更好的是,寫一個擴展方法來做同樣的事情。我們在單元測試中有類似Assert.AreSimiliar(a,b)
的東西。
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
NUnit還爲其Assert.AreEqual方法提供了重載,以允許提供增量。 – BrettMStory 2017-02-14 23:04:51
您可以提供包含兩個值之間可接受差異的參數的函數。例如
// 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)
+1爲正確答案,+1爲推薦擴展名,-1爲不足以使用希臘字母。 :-) – 2010-08-06 03:47:04
我會稱之爲'CloseEnoughForGovernmentWork()',親自 – kyoryu 2010-08-06 03:50:55
比較浮點數的一種方法是比較將它們分開的浮點表示的數量。這個解決方案對數字的大小無關緊要,因此您不必擔心其他答案中提到的「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;
}
}
如果你想比較浮點數,改變所有double
到float
,所有long
到int
和0x8000000000000000
到0x80000000
。
使用representationTolerance
參數,您可以指定容許錯誤的大小。數值越高意味着接受的誤差越大。我通常使用值10作爲默認值。
比較零和非常小的負值如何?似乎它不起作用。例如比較0和-1.1102230246251565E-16 – 2016-09-30 10:16:26
提供的鏈接已過時。新的位置是https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ – 2016-09-30 12:36:37
我不明白你爲什麼從0x800減去負值...可能你請詳細說明這一點? – m93a 2017-12-26 20:24:49
我還沒有檢查添加了哪個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);
問題被問如何斷言某事是在單元測試中幾乎相等。通過使用內置的Assert.AreEqual
函數,您聲明的內容幾乎相同。例如:
Assert.AreEqual(expected: 3.5, actual : 3.4999999, delta:0.1);
此測試將通過。問題解決了,無需編寫自己的功能!
比較浮點數,2012版:https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ - 不是C#版本,你會得到你所有的東西需要 – 2016-09-30 12:40:04