You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归IFFT实现中的缩放错误:原因排查与修复方案

递归FFT/IFFT逆变换缩放错误的问题分析与修复

我用C++实现了一个递归FFT/IFFT,正向FFT运行正常,但逆IFFT的输出存在缩放错误——8点FFT的逆变换结果是原始数据的1/8,无法正确还原。

实现代码

FFT与IFFT实现

#include <complex>
#include <numbers>
#include <ranges>
#include <valarray>
#include <vector>

#include "assert.hpp"

template<typename T = float>
std::valarray<std::complex<T>> fft(std::valarray<std::complex<T>> const& data)
{
    const auto N = data.size();
    ASSERT(!(N & (N - 1)), "FFT length has to be power of two");

    if (N == 1)
        return data;

    auto even = fft(data[std::slice(0, N / 2, 2)]);
    auto odd = fft(data[std::slice(1, N / 2, 2)]);

    auto result = std::valarray<std::complex<T>>(N);
    for (size_t k = 0; k < N / 2; ++k)
    {
        auto t = std::exp(std::complex<T>(0, -2 * std::numbers::pi * T(k) / N));
        result[k] = even[k] + odd[k] * t;
        result[k + N / 2] = even[k] - odd[k] * t;
    }

    return result;
}

template<typename T = float>
std::valarray<std::complex<T>> ifft(std::valarray<std::complex<T>> const& data)
{
    const auto N = data.size();
    ASSERT(!(N & (N - 1)), "FFT length has to be power of two");

    if (N == 1)
        return data;

    auto even = ifft(data[std::slice(0, N / 2, 2)]);
    auto odd = ifft(data[std::slice(1, N / 2, 2)]);

    auto result = std::valarray<std::complex<T>>(N);
    for (size_t k = 0; k < N / 2; ++k)
    {
        auto t = std::exp(std::complex<T>(0, 2 * std::numbers::pi * T(k) / N));
        result[k] = (even[k] + odd[k] * t) / T(N);
        result[k + N / 2] = (even[k] - odd[k] * t) / T(N);
    }

    return result;
}

测试代码

std::valarray<std::complex<float>> data = { 3, 5, 8, 9, 1, 4, 7, 2 };

std::cout << "Data: ";
for (auto const& e : data)
    std::cout << e << " ";
std::cout << "\n";

auto spectrum = fft(data);

std::cout << "Forward FFT: ";
for (auto const& e : spectrum)
    std::cout << e << " ";
std::cout << "\n";

std::cout << "Inverse FFT: ";
auto inverse = ifft(spectrum);
for (auto const& e : inverse)
    std::cout << e << " ";
std::cout << "\n";

运行输出

Data: (3,0) (5,0) (8,0) (9,0) (1,0) (4,0) (7,0) (2,0) 
Forward FFT: (39,0) (-2.24264,-6.65685) (-11,2) (6.24264,-4.65685) (-1,0) (6.24264,4.65685) (-11,-2) (-2.24264,6.65685) 
Inverse FFT: (0.375,0) (0.625,6.58126e-09) (1,2.73196e-09) (1.125,-1.21692e-08) (0.125,0) (0.5,-1.20452e-08) (0.875,-2.73196e-09) (0.25,1.76331e-08)

问题原因

标准逆FFT的总缩放因子应为1/N(对应离散傅里叶逆变换的定义),但当前IFFT实现错误地在每一层递归的组合步骤都除以当前序列长度N,导致总缩放过度:

  • 对于8点序列,递归分为3层(8→4→2),总缩放因子为1/(8*4*2)=1/64
  • 正向FFT的结果X[k]经过逆变换后本应得到N*x[n],当前代码额外多除以了8,最终结果变为x[n]/8

修复方案

方案一:仅在递归结束后整体缩放

将缩放操作移到递归的最后一步,只对最终结果整体除以总长度N:

template<typename T = float>
std::valarray<std::complex<T>> ifft(std::valarray<std::complex<T>> const& data)
{
    const auto N = data.size();
    ASSERT(!(N & (N - 1)), "FFT length has to be power of two");

    if (N == 1)
        return data;

    auto even = ifft(data[std::slice(0, N / 2, 2)]);
    auto odd = ifft(data[std::slice(1, N / 2, 2)]);

    auto result = std::valarray<std::complex<T>>(N);
    for (size_t k = 0; k < N / 2; ++k)
    {
        auto t = std::exp(std::complex<T>(0, 2 * std::numbers::pi * T(k) / N));
        result[k] = even[k] + odd[k] * t;
        result[k + N / 2] = even[k] - odd[k] * t;
    }

    // 仅在最后一步整体除以总长度N
    return result / T(N);
}

方案二:分层缩放(每一层除以2)

将总缩放因子1/N分解到每一层递归中,每一层组合时除以2(因为每一层将N点序列拆分为两个N/2点序列,总层数为log2(N),(1/2)^log2(N)=1/N):

template<typename T = float>
std::valarray<std::complex<T>> ifft(std::valarray<std::complex<T>> const& data)
{
    const auto N = data.size();
    ASSERT(!(N & (N - 1)), "FFT length has to be power of two");

    if (N == 1)
        return data;

    auto even = ifft(data[std::slice(0, N / 2, 2)]);
    auto odd = ifft(data[std::slice(1, N / 2, 2)]);

    auto result = std::valarray<std::complex<T>>(N);
    const T scale = T(0.5); // 每一层缩放1/2
    for (size_t k = 0; k < N / 2; ++k)
    {
        auto t = std::exp(std::complex<T>(0, 2 * std::numbers::pi * T(k) / N));
        result[k] = (even[k] + odd[k] * t) * scale;
        result[k + N / 2] = (even[k] - odd[k] * t) * scale;
    }

    return result;
}

两种方案都能让逆FFT正确还原原始数据,修复缩放错误。

内容的提问来源于stack exchange,提问作者thebear8

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 17:45:54