3

因此,假设我们有一个浮点类型 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;
}
4

4 回答 4

2

要测试两个数字是否在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 = 23

这样做的原因是浮点数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()
)
于 2013-06-29T17:21:12.277 回答
0

考虑到 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));
}
于 2013-06-29T17:08:28.607 回答
0

由于这只是为了调试,可能会松懈并使用简单的测试来检测相对错误,例如:

if (fabs(f1 - f2) <= SomeNumber * fabs(f2)) ThingsAreGood else ThingsAreBad;

这假设这f2是已知的好(或至少已知更好)的值,并且浮点运算中舍入的误差与 成正比f2。请注意,计算可能会以复杂的方式产生错误。例如,如果f1沿途添加和减去各种其他值,则中间值的幅度远大于由 表示的最终结果f2,则舍入误差可能与那些大的中间值成比例,而不是与 成比例f2。在这种情况下,您可能需要基于中间计算而不是基于f2.

于 2013-06-29T14:35:09.493 回答
0

编辑答案中使用的解决方案在我的情况下不适用于大量比较。

我用给定的数字精度编写了一个使用字符串比较的函数

#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;
}
于 2020-03-24T17:21:17.350 回答