我正在为数字信号处理编写信号源,发现了奇怪的行为.以下是代码:

    float complex *output = malloc(sizeof(float complex) * 48000);
    FILE *f = fopen("test_signal.cf32", "wb");
    int64_t freq = -3355;
    float phase_increment = 2 * (float) M_PI * (float) freq / 48000;
    float phase = 0.0F;
    for (int i = 0; i < 150 * 5; i++) {
        for (int j = 0; j < 9600; j++) {
            output[j] = cosf(phase) + sinf(phase) * I;
            phase += phase_increment;
        }
        fwrite(output, sizeof(float complex), 9600, f);
    }
    fclose(f);

它将创建一个复杂信号从中心偏移-3355hz的文件.所以,当我在这个文件上运行FFT时,我期望信号为-3355hz.由于量化,在这个数字周围有一些微小的频率分布.但实际上,我得到了以下信息:

enter image description here

在50秒左右有一个相当明显的频率跳变(~2000hz).

Anyone knows why?

我的实验表明:

推荐答案

一旦phase变得足够大,它的增量将通过浮点舍入进行量化,这可能足以使舍入到最近值与舍入到偶数尾数的平局中断无法平衡,相反,这意味着你将持续推进无相位.见wikipedia's article on single-precision IEEE float

一个以10为基数的类比,如果数值限制在4个有效数字以内,那么101.3.2 + 0.111只会上升到101.3,而不是101.311.(计算机使用二进制浮点数,所以实际上像101.3.125这样的东西完全可以表示,而101.3.1则不能.)

我猜就是这样.你不会溢出到+Inf,但考虑到float的相对精度有限,在一个大数字上加一个小数字最终将根本不会移动它:最接近phase + phase_increment的可表示浮动将是原来的phase.

A quick way to test this hypothesis would be to use 100(不做任何其他更改),看看这是否移动了获得频率步长的点.(要大得多,可能已经过了你所创造的终点).

(哦,刚刚注意到你已经这么做了,而且确实很有帮助.如果你是说它完全解决了问题,那么很可能就是它.)

您仍然可以保留单一精度sinfcosf,尽管更改它们可能是另一件需要try 的事情:正如@Weather Vane所指出的,一些sin种实现在射程缩减方面失go 了显著的精度.但你可能会认为这是一个噪音更大的信号,而不是频率的一致变化(这需要相位的比例因子)

你也可以让它在float的情况下运行更长的时间,看看你是否在频率上又降低了一步,或者一路改变到直流(恒定相位,频率=0).

或者使用float,手动展开,使两个计数器偏移phase_increment,并将每个计数器增加2*phase_increment.因此,phase与所加数值的相对大小并没有变得如此极端.不过,它们仍然会达到相同的绝对量级,相对于最初的phase_increment,它们会更大,所以仍然会有一些舍入.


我不知道是否有更好的策略来产生复杂的正弦波,我只是想解释一下你看到的行为.

对于该方法的性能,如果编译器没有优化对这两个值的调用,您可能希望同时计算这两个值.(希望通过调用SIMD sincosf自动矢量化.)

C++相关问答推荐

使用额外的公共参数自定义printf

GCC预处理宏和#杂注GCC展开

为什么可以在typedef之前使用typedef d struct 体?

我无法让LLDB正确运行我的可执行文件

使用C时,Windows CMD中的argc参数是否包含重定向命令?

为什么将函数名括在括号中会禁用隐式声明?

OpenSSL:如何将吊销列表与SSL_CTX_LOAD_VERIFY_LOCATIONS一起使用?

Boyer Moore算法的简单版本中的未定义行为

为 struct 中的数组动态分配内存时出错

Cairo STM32MP1 cairo_Surface_WRITE_TO_PNG始终返回CAROLIO_STATUS_WRITE_ERROR

如何使用FSeek和文件流指针在C中查找文件的前一个元素和前一个减go 一个元素

为什么realloc函数在此代码中修改变量?

如果格式字符串的内存与printf的一个参数共享,会发生什么情况?

Fscanf打印除退出C代码为1的程序外的所有内容

C23标准是否向后兼容?

我正在使用c学习数据 struct ,在学习堆栈时,我试图将中缀转换为后缀,并编写了这段代码.代码未给出输出

为什么这个代码的最后一次迭代不能正常工作?

UpDown控制与预期相反

无法理解 fgets 输出

单线程代码中的编译器屏障是否必要?