关于c ++:当值变小时,为什么这些计算需要多20倍的时间

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

本问题已经有最佳答案,请猛点这里访问。

我是一个电路设计师,而不是软件工程师,所以我不知道如何追踪这个问题。

我正在处理一些IIR过滤器代码,当我通过过滤器处理非常小的值时,我遇到了执行速度非常慢的问题。为了找到问题,我编写了这个测试代码。

通常,循环将在大约200毫秒左右的时间内运行。(我没有测量它。)但是当testcheckbox->checked时,运行大约需要7秒。问题在于循环中A、B、C和D的大小减小,这正是IIR过滤器中输入变为零后的值所发生的情况。

我认为问题在于变量的exponent值小于-308。一个简单的修复方法是将变量声明为longdouble,但在实际代码中这不是一个简单的修复方法,而且看起来我不需要这样做。

你知道为什么会发生这种情况,还有什么简单的解决办法?

在它的情况下,我使用C++ Builder XE3。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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;
}

编辑:正如答案所说,这个问题的原因是非正规数学,这是我从未听说过的。维基百科有一个很好的描述,就像sneftel给出的msdn文章一样。

http://en.wikipedia.org/wiki/denormal_编号

说了这句话,我仍然不能让我的代码刷新非正常值。msdn文章说要做到这一点:

1
_controlfp(_DN_FLUSH, _MCW_DN)

但是,这些定义不在XE3数学库中,所以我使用

1
controlfp(0x01000000, 0x03000000)

根据文章,但这对XE3没有影响。维基百科的文章中也没有建议的代码。

有什么建议吗?


你遇到了非正规数(小于DBL_MIN的数,其中最重要的数字被视为零)。非规范化扩展了可表示浮点数的范围,对于保持FP算法中某些有用的误差界很重要,但对它们的操作要比对普通FP数的操作慢得多。它们的精度也较低。所以你应该尽量保持你所有的数字(中间和最终的数量)大于dbl_min。

为了提高性能,可以通过调用_controlfp(_DN_FLUSH, _MCW_DN)(或者,根据操作系统和编译器的不同,调用类似的函数)强制将非规范化刷新为零。http://msdn.microsoft.com/en-us/library/e9b52ceh.aspx


您已经进入了浮点下溢的领域,这会导致非规范化的数字——这取决于您可能陷入软件的硬件,而软件的速度要比硬件操作慢得多。