簡體   English   中英

比較 C 中的浮點數以進行單元測試

[英]comparing floating point numbers in C for unit testing

所以我使用 CUnit 進行單元測試。 我希望像

float x;
x = atof("17.99");

我想用斷言來測試它; 顯然用一些小的epsilon我可以

CU_ASSERT(abs(x - atof("17.99")) < epsilon);

不過我正在考慮

r = atof("17.99");
CU_ASSERT(0 == memcmp(&x, &r, sizeof(float));

這似乎確實有效。 我希望使用它來避免必須根據值在每個測試上手動設置 epsilon。 在上述情況下 1e-6 應該就足夠了; 但是,如果值是 1e-10,使用 1e-6 的 epsilon 可能不會出現問題。 開發人員必須做出的選擇越多,出錯的空間就越大。

我的問題是:這種技術在 posix 系統上是否應該穩定? 也就是說,如果要比較的兩個浮點數是由完全相同的步驟生成的,則它們的內部表示應該完全相同。

編輯:更重要的是我最終想要一個 CU_ASSERT_FLOAT_EQUAL 宏。

比較浮點值很困難。 混合字符串不會讓事情變得更好,當然也不會像 epsilon 那樣引入少量的回旋余地。

看看這篇文章: 比較浮點數,2012 年版 對於我的錢,ULP 是要走的路。 有一些令人討厭的邊緣情況,但您可能可以忽略其中的大部分。

編輯:5 月 31 日考慮了更多,我認為您要問的是“如果在測試函數和被測函數中使用完全相同的步驟來計算浮點數,答案是否完全相同 -這樣我就不必擔心 +/- 一些小錯誤?”

答案是肯定的,它們將是相同的。 但是在那種情況下,您使單元測試變得毫無意義,因為如果被測代碼中存在錯誤,那么測試函數中必然會出現相同的錯誤。

順便說一句,不要因為使用 memcmp(&x, &y, sizeof(x)) 而分心。 這只是測試在兩個值中設置的位完全相同。 如果這是真的,那么 x == y 必然是真的。 堅持 x == y。

小心將浮點數與雙精度數進行比較,就像在最初的問題中所做的那樣。 浮點數必然會失去精度,因此當將它與全精度雙精度數進行比較時,您可能會發現數字不相等,即使計算是完美的。

有關詳細信息,請參閱http://randomascii.wordpress.com/2012/06/26/doubles-are-not-floats-so-dont-compare-them/

float r, x;

r = atof("17.99");
CU_ASSERT(0 == memcmp(&x, &r, sizeof(float));

應該和效果一樣

r = atof("17.99");
CU_ASSERT(x == r);

注意 atof 返回一個雙精度值,所以

CU_ASSERT(x == atof("17.99"));

是不同的,但是

CU_ASSERT(x == (float)atof("17.99"));

也應該是一樣的。

另請注意,當使用從 x87 繼承的指令時,gcc 優化器在 x86 上有一個長期存在的錯誤(如果我沒記錯的話,這不會發生在 x86_64 上)。

所以答案似乎是否定的。

原因是即使您在計算每個變量時使用相同的一系列計算,如果計算值的步驟之間有任何代碼,您可能會導致不同的舍入誤差,如@AProgrammer's response 中的注釋所述。

問題是,雖然您可能聲明一個 n 位浮點數,但它可能存儲在更大的寄存器中(x87 使用 80 位寄存器)。 如果將值從寄存器推送到內存中以釋放寄存器用於其他操作,則該值將被截斷(四舍五入?我的筆記去哪里了......)。 當該值被帶回寄存器時,失去精度的將完成其余的計算。

另一方面,另一段代碼在計算值時可能會經歷完全相同的步驟; 但是,如果該值沒有從寄存器中推出(或在不同的地方推出......),那么當它再次存儲在內存中時,您會得到不同的截斷。

根據 gcc 郵件列表/錯誤報告中的注釋,所有這些都是 IEEE 批准的。

因為自從 80386 以來我就沒有接觸過寄存器,所以我只能猜測現代 x86 和 amd_64 處理器有什么; 但我猜測 gcc 沒有提示,對於 x86,它使用的是基本的 x87 寄存器集或基本的 SSE 寄存器集。

所以使用 fabs(xy) < epsilon; 的經驗法則。 持有,正如@Martin Beckett 評論的帖子所指出的那樣,CUnit 以雙格式提供(如果有人想像我習慣的那樣對事物進行分析,可以輕松地編寫宏的浮動版本)在。

我認為有一種不同的方式來解決這個問題。 問題是關於單元測試。 單元測試應該測試和記錄被測單元 (uut)。

為此,在編寫測試時應詢問 uut 可接受的容差是多少。 (這可能需要針對每個 uut 而不是跨測試項目考慮)。

通過這種方式,測試可以避免測試相等性,測試該值是否在可接受的范圍內,以及記錄單元測試結果的可接受容差。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM