[英]How can I check whether two numbers are within “x” significant figures of the precision limits of the floating point type?
因此,假設我們有一個浮點型 XType,其中有兩個數字:
XType const a = 1.2345
XType const b = 1.2300
然后我想要一個函數 IsClose(XType const f1,XType const f2,unsigned const truncated_figures) 這樣
// the numbers are equal if the last two figures are ignored (1.23 == 1.23)
IsClose<XType>(a,b,2) == true
// the numbers are not equal if only the last is ignored (1.234 != 1.230)
IsClose<XType>(a,b,1) == false
到目前為止,我有這個丑陋的爛攤子,但我還沒有說服自己這是正確的:
// check if two floating point numbers are close to within "figures_tolerance" figures of precision for the applicable type
template <typename FloatType>
bool const IsClose(FloatType const f1, FloatType const f2, unsigned const figures_tolerance)
{
FloatType const tolerance_exponent = std::pow(10.0,figures_tolerance);
FloatType const tolerance =
std::pow(tolerance_exponent,std::log10(f1)) *
std::numeric_limits<FloatType>::epsilon()
;
return std::abs(f1 - f2) < tolerance;
}
我的推理是,容差應該是將 epsilon 提高到數量超過或低於 1.0(epsilon 所基於的有效數字)的數量級。 這有意義嗎? 有沒有更好、更可靠的方法?
編輯:我使用模板功能的解決方案如下(它基於下面 user763305 的回答)
// check if two floating point numbers are within the last n digits of precision for the
// largest of the two numbers being compared.
template <typename FloatType>
bool const IsWithinPrecision(FloatType const f1, FloatType const f2, unsigned const n = 1U)
{
FloatType const f_ref = std::max(std::abs(f1), std::abs(f2));
FloatType const distance = std::abs(f1 - f2);
FloatType const e = std::numeric_limits<FloatType>::epsilon();
return distance < std::pow((FloatType) 10.0, (FloatType) n) * e * f_ref;
}
要測試兩個數字是否在彼此的n
有效數字內,請使用不等式
abs(a - b) < pow(0.1, n) * max(abs(a), abs(b))
但是,我通常發現測試有效數字的數量是否至少是有效數字的最大可能數量(考慮到浮點類型的精度)減去n
更有用。 這可以使用不等式來完成
abs(a - b) < pow(10.0, n) * std::numeric_limits<...>::epsilon() * max(abs(a), abs(b))
換句話說, n
是我們因舍入錯誤而丟失的有效數字的數量。 像n = 2
或3
這樣的東西通常在實踐中起作用。
之所以這樣的作品是一個浮點數之間的距離a
和下一個可表示浮動低於和高於點數a
間謊言
0.5 * std::numeric_limits<...>::epsilon() * abs(a)
和
std::numeric_limits<...>::epsilon() * abs(a)
此外,如果您正在處理非常小的或更准確的非正規數,則上述不等式不起作用。 那么你應該使用不等式
abs(a - b) < pow(10.0, n) * max(
std::numeric_limits<...>::epsilon() * max(abs(a), abs(b)),
std::numeric_limits<...>::denorm_min()
)
由於這只是為了調試,可能會松懈,使用簡單的相對誤差測試,例如:
if (fabs(f1 - f2) <= SomeNumber * fabs(f2)) ThingsAreGood else ThingsAreBad;
這假設f2
是已知好的(或至少已知更好的)值,並且浮點運算中舍入的誤差與f2
成正比。 請注意,計算可能會以復雜的方式產生錯誤。 例如,如果在此過程中將各種其他值添加到f1
或從f1
減去,因此中間值的幅度比f2
表示的最終結果大得多,則舍入誤差可能與那些較大的中間值成正比,而不是與f2
成正比. 在這種情況下,您可能需要根據中間計算而不是f2
來計算錯誤閾值。
考慮到 Eric Postpischil 指出的情況,這個函數根據精度告訴 2 個數字是否足夠接近。
bool const IsClose(FloatType const f1, FloatType const f2, unsigned const figures_tolerance)
{
FloatType res = f1-f2;
res = res*pow(10.0,figures_tolerance);
return !bool(int(res));
}
編輯答案中使用的解決方案在我的情況下不適用於大量比較。
我使用給定數字精度的字符串比較編寫了一個函數
#include <iomanip>
/**
* Compare two number with a given digit precision
*
* @tparam T - Number precision
*
* @param n1 - First number to compare
* @param n2 - Second number to compare
* @param n - The first n digits that must be equals between the two numbers
*
* @return True if the n first digits of the two numbers are equals, false otherwise
*/
template<typename T>
bool isEqual(T n1, T n2, int n)
{
int index = 0;
std::ostringstream a, b;
a << std::setprecision(n);
b << std::setprecision(n);
std::cout << std::setprecision(n);
a << std::fixed;
b << std::fixed;
std::cout << std::fixed;
a << n1;
b << n2;
while (a.str()[index] == b.str()[index] && index < n) {
index++;
}
if (index != n) {
std::cout << "n1 != n2\n\nn1 = " << a.str() << "\nn2 = " << b.str() << "\ndiffer at index " << index << std::endl;
}
return index == n;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.