4

I was going through a code base and found following template function:

template <class T, class T2>
T mix(const T &a, const T &b, const T2 &interp) {
  static constexpr T2 one = ((T2)1);
  return (a * (one - interp)) + (b * interp);
}

And following comment

// You'd think instead of doing the a*(1-t) + b*t, it'd be faster
// and one less multiply to do a + (b-a)*t, right? Bad! Increases floating
// point exception occurances. Same as LERP

Can someone highlight why that should be true?

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
user1767754
  • 23,311
  • 18
  • 141
  • 164

1 Answers1

3

As Paul R said in comments, the comments are probably referring to denormal (I more usually see them described as subnormal, so will use that term) numbers, which fill the underflow gap around zero in floating point arithmetic.

If a and b are sufficiently close in value, then a-b will produce a subnormal value. When those values are handled entirely in hardware, there is often a performance hit. There are techniques to mitigate that in hardware but, with some modern processors, instructions involving subnormals can take over 100 times longer than the same instructions acting on normal values. If those values are handled entirely in software (e.g. the hardware instructions don't handle them directly, and a floating point exception is raised that has to be caught and then sorted out in software) there is virtually always a decrease in performance.

Depending on the type of application, the resultant issues can vary from the insignificant (e.g. a few extra milliseconds for a long numeric calculation that doesn't encounter subnormals too often) to the major (e.g. introducing a potential timing side channel into a security-related system).

The solution given in the question does rely on interp being neither a subnormal itself, not too close to 1.0.

Peter
  • 35,646
  • 4
  • 32
  • 74