我正在為數字信號處理撰寫信號源,并發現了奇怪的行為。這是代碼:
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。由于量化,該數字周圍有一些較小的頻率分布。但實際上我得到以下資訊:

在 50 秒左右有一個非常顯著的頻率跳躍(~2000hz)。
有誰知道為什么?
我的實驗表明:
- 使用 double 作為
phase幫助 - 將引數從 -2pi 減少到 2pi 有幫助
- 蘋果M1和樹莓派一樣跳
phase論點似乎沒有溢位- 有趣的閱??讀https://randomascii.wordpress.com/2014/10/09/intel-underestimates-error-bounds-by-1-3-quintillion/但不太可能導致頻率突然跳躍
uj5u.com熱心網友回復:
一旦phase變得足夠大,它的增量就會通過浮點舍入來量化,也許足以使舍入到最接近的舍入到甚至尾數的平局不會平衡,而是意味著你始終推進階段更少。參見維基百科關于單精度 IEEE 浮點數的文章
值限制為 4 個有效數字的以 10 為底的類比101.2 0.111只會上升到101.3,而不是101.311。(計算機使用二進制浮點數,所以實際上像 101.125 這樣的東西在 101.1 不是的地方完全可以表示。)
我懷疑這就是正在發生的事情。您不會溢位到 Inf,但鑒于 a 的相對精度有限float,將一個小數添加到一個巨大的數字最終不會移動它:最接近的可表示浮點數phase phase_increment將是原始的phase.
測驗這個假設的一種快速方法是使用double phase(不做任何其他更改),看看它是否移動了你獲得頻率步長的點。(要大得多,可能超過了你所產生的東西)。
(哦,剛剛注意到您已經這樣做了,并且確實有所幫助。如果您的意思是它完全消除了問題,那么可能就是這樣。)
您仍然可以保持單精度sinf和cosf,盡管更改它們將是另一件事:正如@Weather Vane 指出的那樣,一些sin實作在范圍縮小方面失去了顯著的精度。但是您會期望這是一個更嘈雜的信號,而不是頻率的一致變化(這將需要相位的比例因子。)
您也可以讓它運行更長時間,float看看您是否會進一步降低頻率,或者一直更改為 DC(恒定相位,頻率 = 0)。
或者使用float手動展開,使您有兩個計數器偏移,phase_increment并且每個計數器遞增2*phase_increment。因此,與添加內容的相對大小phase并沒有變得如此極端。但是,它們仍然會達到相同的絕對量級,相對于原始量級來說很大phase_increment,所以仍然會有一些舍入。
我不知道是否有更好的策略來生成復雜的正弦波,我只是想解釋你所看到的行為。
對于此方法的性能sincosf,如果您的編譯器沒有優化您的呼叫,您可能希望同時計算這兩個值。(并希望通過呼叫 SIMD 自動矢量化sincosf。)
uj5u.com熱心網友回復:
您觀察到的是浮點精度問題。浮點數的精度隨幅度而降低。
幸運的是,對于相位或類似的角度值,有一個簡單的解決方法,將值包裹在 2π 周圍,這樣幅度永遠不會太大。
在你之后添加以下代碼phase = phase_increment;,看看是否有幫助。
if( phase < (float)( 2.0 * M_PI ) )
continue;
phase -= (float)( 2.0 * M_PI );
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/477694.html
