Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
264 views
in Technique[技术] by (71.8m points)

c++ - How can I check whether two numbers are within "x" significant figures of the precision limits of the floating point type?

So suppose we have a float type XType in which we have two numbers:

XType const a = 1.2345
XType const b = 1.2300

Then I want a function IsClose(XType const f1,XType const f2,unsigned const truncated_figures) such that

// 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

So far I have this ugly mess, but I'm yet to convince myself it's correct:

// 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;
}

My reasoning is that the tolerance should be the epsilon raised to the order of magnitude that the number exceeds or subseeds 1.0 (the significant figures for which the epsilon is based). Does this make sense? Is there a better, more reliable way?

EDIT: My solution using the template function is below (it is based on user763305's answer below)

// 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;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

To test whether two numbers are within n significant digits of each other, use the inequality

abs(a - b) < pow(0.1, n) * max(abs(a), abs(b))

However, I usually find it more useful to test if the number of significant digits is at least the maximum possible number of significant digits (given the precision of the floating point type) minus n. This can be done using the inequality

abs(a - b) < pow(10.0, n) * std::numeric_limits<...>::epsilon() * max(abs(a), abs(b))

In other words, n is the number of significant digits we have lost through rounding errors. Something like n = 2 or 3 usually works in practice.

The reason this works is that the distances between a floating point number a and the next representable floating point numbers below and above a lie between

0.5 * std::numeric_limits<...>::epsilon() * abs(a)

and

std::numeric_limits<...>::epsilon() * abs(a)

Also, the above inequality does not work if you are dealing with very small, or more precisely, denormal numbers. Then you should instead use the inequality

abs(a - b) < pow(10.0, n) * max(
    std::numeric_limits<...>::epsilon() * max(abs(a), abs(b)),
    std::numeric_limits<...>::denorm_min()
)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...