<< >>
justin = { main feed , music , code , askjf , pubkey };
[ <<< last (older) article | view in index | next (newer) article >>> ]

July 14, 2011
Fun with denormals

Something that often comes up when writing audio applications are things called "denormals". These are floating point numbers that are very small, so small in fact that for some reason CPU designers think they are quite rare (OK so they are), so the circuitry that processes them is very slow, when compared to that of a "normal" sized number.

If you read that (awful) explanation, and didn't understand it or care, I apologize, you can stop reading now because it will only get more boring and less intelligible.

We have made some common code to filter out these numbers, as many others no doubt have done:

// boring code omitted, see WDL/denormal.h for the full code
// WDL_DENORMAL_DOUBLE_HW() is a macro which safely gets you the high 32 bits of a double as an unsigned int.

static double WDL_DENORMAL_INLINE denormal_filter_double(double a)
{
  return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0;
}
The above code pretty simply looks at the exponent field of the double, and if it is nonzero, returns the double, otherwise it returns 0.

Recently it came to our attention that we actually needed to filter out larger numbers as well (when sending those numbers through a process such as a FFT, they would end up as denormals). If we pick a number around 10^-16 (not being picky about the exact cutoff), which has an exponent of 0x3C9, we can choose to filter when the expoenent field is under that value:
static double WDL_DENORMAL_INLINE denormal_filter_double_aggressive(double a)
{
  return ((WDL_DENORMAL_DOUBLE_HW(&a))&0x7ff00000) >= 0x3c900000 ? a : 0.0;
}
That was pretty much free (ok slightly larger code, I suppose). One nice thing that became apparent was that we could filter NaN and infinity values this way as well (exponent == 0x7FF), with only the cost of a single integer addition:
static double WDL_DENORMAL_INLINE denormal_filter_double_aggressive(double a)
{
  return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= 0x3cA00000 ? a : 0.0;
}
Note that the exponent is increased by 1, so that 0x7FF becomes 0, and we adjusted the cutoff constant for the change.

An extra thought: if you need to pick the cutoff number more precisely, you could change the mask to 0x7fffffff and the cutoff (0x3cA00000) to include some of the fraction digits...

Additional reading: IEEE_754-1985.

Oh and happy bastille day!






3 Comments:
Posted by Tale on Fri 15 Jul 2011 at 12:12 from 77.168.115.x
I have been using denormal.h in ComboV for some time now, and it works really well. :) The new agressive versions might also be useful for detecting (near) silence.

BTW, I think I have found a minor bug/typo in denormal.h:

#define WDL_DENORMAL_DOUBLE_LW(a) (((const WDL_DenormalDoubleAccess*)(a))->w.hw)

Shouldn't that read "->w.lw" instead of "->w.hw"? The same goes for WDL_DENORMAL_DOUBLE_LW_NC.

I have a modified denormal.h in my own WDL repository. It contains the two typo fixes, and I have added a couple of detecting macros/functions, that just detect denormals/zeroes without changing anything. If you want I could send you a diff patch with these changes (or you could download it straight from my WDL repository).


Posted by Justin on Fri 15 Jul 2011 at 13:43 from 74.66.229.x
oops, thanks! I'll fix (it affects the GetMaxAbsValue functions, doh!)


Posted by Tale on Fri 15 Jul 2011 at 18:33 from 86.87.246.x
Cool!

Yeah, it's only used in the peak tracking stuff, and it's the least-significant part, so one would perhaps not even notice it.


Add comment:
Name:
Human?: (no or yes, patented anti crap stuff here)
Comment:
search : rss : recent comments : Copyright © 2019 Justin Frankel