Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does it require 20X more time for these calculations when the values get tiny [duplicate]

I am a circuit designer, not a software engineer, so I have no idea how to track down this problem.

I am working with some IIR filter code and I am have problems with extremely slow execution times when I process extremely small values through the filter. To find the problem, I wrote this test code.

Normally, the loop will run in about 200 ms or so. (I didn't measure it.) But when TestCheckBox->Checked, it requires about 7 seconds to run. The problem lies with the reduction in size of A, B, C and D within the loop, which is exactly what happens to the values in an IIR filter after it's input goes to zero.

I believe the problem lies with the fact that the variable's expononent value becomes less than -308. A simple fix is to declare the variables as long doubles, but that isn't an easy fix in the actual code, and it doesn't seem like I should have to do this.

Any ideas why this happens and what a simple fix might be?

In case its matters, I am using C++ Builder XE3.

int j;
double A, B, C, D, E, F, G, H;
//long double A, B, C, D, E, F, G, H; // a fix
A = (double)random(100000000)/10000000.0 - 5.0;
B = (double)random(100000000)/10000000.0 - 5.0;
C = (double)random(100000000)/10000000.0 - 5.0;
D = (double)random(100000000)/10000000.0 - 5.0;
if(TestCheckBox->Checked)
 {
  A *= 1.0E-300;
  B *= 1.0E-300;
  C *= 1.0E-300;
  D *= 1.0E-300;
 }
for(j=0; j<=1000000; j++)
{
 A *= 0.9999;
 B *= 0.9999;
 C *= 0.9999;
 D *= 0.9999;
 E = A * B + C - D; // some exercise code
 F = A - C * B + D;
 G = A + B + C + D;
 H = A * C - B + G;
 E = A * B + C - D;
 F = A - C * B + D;
 G = A + B + C + D;
 H = A * C - B + G;
 E = A * B + C - D;
 F = A - C * B + D;
 G = A + B + C + D;
 H = A * C - B + G;
}

EDIT: As the answers said, the cause of this problem is denormal math, something I had never heard of. Wikipedia has a pretty nice description of it as does the MSDN article given by Sneftel.

http://en.wikipedia.org/wiki/Denormal_number

Having said this, I still can't get my code to flush denormals. The MSDN article says to do this:

_controlfp(_DN_FLUSH, _MCW_DN) 

These definitions are not in the XE3 math libraries however, so I used

controlfp(0x01000000, 0x03000000) 

per the article, but this is having no affect in XE3. Nor is the code suggested in the Wikipedia article.

Any suggestions?

like image 458
user5108_Dan Avatar asked Oct 31 '14 14:10

user5108_Dan


2 Answers

You're running into denormal numbers (ones less than DBL_MIN, in which the most significant digit is treated as a zero). Denormals extend the range of the representable floating-point numbers, and are important to maintain certain useful error bounds in FP arithmetic, but operating on them is far slower than operating on normal FP numbers. They also have lower precision. So you should try to keep all your numbers (both intermediate and final quantities) greater than DBL_MIN.

In order to increase performance, you can force denormals to be flushed to zero by calling _controlfp(_DN_FLUSH, _MCW_DN) (or, depending on OS and compiler, a similar function). http://msdn.microsoft.com/en-us/library/e9b52ceh.aspx

like image 187
Sneftel Avatar answered Sep 27 '22 00:09

Sneftel


You've entered the realm of floating-point underflow, resulting in denormalized numbers - depending on the hardware you're likely trapping into software, which will be much much slower than hardware operations.

like image 24
sfjac Avatar answered Sep 24 '22 00:09

sfjac