c中的高精度离散傅里叶变换

计算科学 C 精确 傅里叶变换
2021-12-15 17:00:38

我正在尝试对信号进行高精度离散傅立叶变换。为了检查精度,我使用高斯函数作为信号,因为傅立叶变换也是高斯函数。 使用 Mathematica 对高斯函数进行傅里叶变换

代码如下

#include <stdio.h>
#include <tgmath.h>

typedef long double complex cplx;
long double PI;
void _fft(cplx buf[], cplx out[], int n, int step)
{
if (step < n) {
    _fft(out, buf, n, step * 2);
    _fft(out + step, buf + step, n, step * 2);

    for (int i = 0; i < n; i += 2 * step) {
        cplx t = cexpl(- I * PI * i / n) * out[i + step];
        buf[i / 2]     = out[i] + t;
        buf[(i + n)/2] = out[i] - t;
    }
}
}

void fft(cplx buf[], int n)
{
cplx out[n];
for (int i = 0; i < n; i++) out[i] = buf[i];

_fft(buf, out, n, 1);
}


int main()
{
const int nPoints = pow(2, 12);
PI = atan2(1, 1) * 4;
cplx dt = 1e-3;
cplx dOmega = 1 / (dt * nPoints);
long double T[nPoints];
long double DOmega[nPoints];

cplx At[nPoints];
cplx tau = 28.4e-3;
for (int i = 0; i < nPoints; ++i)
{
    T[i] = dt*(i-nPoints/2);
    DOmega[i] = dOmega * (i - nPoints / 2);
    At[i] = cexpl(-T[i]*T[i]/2/(tau*tau));
}

fft(At, nPoints);
FILE* fw;
fw = fopen("fft_01.txt", "w+");

for (int i = 0; i < nPoints; ++i)
{
    fprintf(fw, "%.15Le, %.15Le\n", DOmega[i], fabs(At[i]) );
}

return 0;
}

当我将定义的类型从 float complex 更改为 double complex 时,结果确实显示了精度的提高。但是,当我将数据类型从双复数更改为长双复数时,虽然数字远离最小限制,但没有任何改进。

浮点数据的结果 使用双数据的结果 使用长双精度数据的结果

我认为峰值的两个翅膀应该像 Mathematica 的结果一样下降,但这里似乎卡在 10^(-35) 附近,无法进一步下降。

当我进一步将 iDFT 应用于 DFT 的结果时,更明显的是 long double 精度已丢失。蓝色的是原始信号,精度高。绿色的是iDFT(DFT(x))之后的结果,应该与蓝色的一致,但最小的数字要大得多。 iDFT(DFT(x)) 的结果

谁能告诉我问题出在哪里,在此先感谢。

PS:我在 Mac OS 10.13.1 上,gcc用作编译器。我已经检查了我平台上的数字限制

storage size for float: 4
minimum float positive value: 1.175494e-38
Maximum float positive value: 3.402823e+38
precision value: 6

storage size for double: 8
minimum double positive value: 2.225074e-308
Maximum double positive value: 1.797693e+308
precision value: 15

storage size for long double: 16
minimum long double positive value: 3.362103e-4932
Maximum long double positive value: 1.189731e+4932
precision value: 18
1个回答

除了指出大多数内置函数(例如atanexpdouble precision变量进行操作)的各种评论之外,还有一个问题是,long double在典型的 x86 类型硬件上,实际上并不比常规double的 . 特别是,long double它不是Mathematica 使用的那种任意精度的数据类型——Mathematica 允许您根据需要选择变量的精度,并且它会以这种精度计算事物,但 C 的long double数据不是这种情况具有固定精度的类型,就像double.