itkMath FloatAlmostEqual question

It looks like FloatAlmostEqual function (itkMath.h) does rather similar thing twice, in particular if floats are not almost equal - subtract, abs, compare (first to epsilon, then unit of least precision), isn’t it? It is not wrong, but may be it could impact performance?

FloatAlmostEqual

I think this is intended. A pair of floating point numbers could have a large absolute difference, and still be considered “almost equal”:

EXPECT_TRUE(
itk::Math::FloatAlmostEqual(
  1.7976931348623158e+308, // <== Which is DBL_MAX.
  1.7976931348623156e+308  // <== Mind the last decimal digit!
));

In this case, the absolute difference is ~2.0e+292, while these two numbers only differ by one unit of least precision (ULP).

On the other hand, a pair of floating point numbers could have a large absolute ULP difference, and still be considered “almost equal”:

EXPECT_TRUE(
itk::Math::FloatAlmostEqual(
  2.2250738585072014e-308, // <== Which is DBL_MIN.
  0.0
));

My debugger tells me that DBL_MIN is 4503599627370496 ULPs apart from 0.0, yet these numbers could be considered almost equal, right?

That’s possible, of course. But do you have a specific application or image filter in mind, whose performance could be affected by this function?

2 Likes

Thank you for explanation.

But do you have a specific application or image filter in mind, whose performance could be affected by this function?

./Modules/Numerics/Statistics/include/itkHistogramToTextureFeaturesFilter.hxx:  if( Math::FloatAlmostEqual( pixelVarianceSquared, 0.0, 4, 2*NumericTraits<double>::epsilon() ) )
./Modules/Filtering/Thresholding/include/itkOtsuMultipleThresholdsCalculator.hxx:         !Math::FloatAlmostEqual( maxVarBetween, varBetween, maxUlps) )
./Modules/Filtering/ImageFrequency/include/itkFrequencyBandImageFilter.hxx:    if ( Math::FloatAlmostEqual(scalarFrequency, this->m_LowFrequencyThreshold) )
./Modules/Filtering/ImageFrequency/include/itkFrequencyBandImageFilter.hxx:    if ( Math::FloatAlmostEqual(scalarFrequency, this->m_HighFrequencyThreshold) )
./Modules/Filtering/Path/include/itkPolyLineParametricPath.hxx:  if ( input > endPoint || itk::Math::FloatAlmostEqual( input, endPoint ) )
./Modules/Filtering/FastMarching/include/itkFastMarchingImageFilterBase.hxx:    if (itk::Math::FloatAlmostEqual<double>(cc, 0.0))
./Modules/Core/Common/include/itkVersor.hxx:  if ( Math::FloatAlmostEqual<T>(vectorNorm, 0.0) )
./Modules/Core/Transform/include/itkBSplineTransform.hxx:    if( Math::FloatAlmostEqual( index[j], maxLimit, 4 ) )
./Modules/Core/Transform/include/itkVersorRigid3DTransform.hxx:  if (Math::FloatAlmostEqual<TParametersValueType>(norm, 0.0))

Edit: removed calls from tests

1 Like

Interesting. I think you’re right: it seems to me that in some of these cases, the performance could be improved by doing something else than calling the current FloatAlmostEqual function. For example, instead of FloatAlmostEqual(x, 0.0), I think I would prefer something like abs(x) < DBL_MIN. But I haven’t had a close look yet at these particular cases.

1 Like

You are right, in some cases FloatAlmostEqual could be replaced by less general std::abs(x)<epsilon. But as that case is checked first in FloatAlmostEqual, and it should be in the predicted branch, the extra code in FloatAlmostEqual should not matter. Of course, to test this assumption a change should be made and performance compared with and without the change.

2 Likes