C++ 傅立叶变换浮点问题
我正在为图像实现一种传统的(也就是说不快的)分离傅里叶变换。我知道,在浮点运算中,等距样本中一个周期的sin或cos之和并不是完全为零,这是常规变换比快速变换更大的问题 该算法适用于二维双阵列,是正确的。反向是在内部完成的(使用非对称公式时通过双符号标志和条件检查),而不是在外部执行共轭。结果几乎100%符合预期,因此这是一个关于细节的问题: 当我执行正向变换时,将对数幅值和角度保存到图像,重新加载它们,然后执行逆变换,我会遇到不同类型的舍入误差,使用不同类型的实现公式:C++ 傅立叶变换浮点问题,c++,math,floating-point,fft,C++,Math,Floating Point,Fft,我正在为图像实现一种传统的(也就是说不快的)分离傅里叶变换。我知道,在浮点运算中,等距样本中一个周期的sin或cos之和并不是完全为零,这是常规变换比快速变换更大的问题 该算法适用于二维双阵列,是正确的。反向是在内部完成的(使用非对称公式时通过双符号标志和条件检查),而不是在外部执行共轭。结果几乎100%符合预期,因此这是一个关于细节的问题: 当我执行正向变换时,将对数幅值和角度保存到图像,重新加载它们,然后执行逆变换,我会遇到不同类型的舍入误差,使用不同类型的实现公式: F(u,v)=和(x=
sum_re += f_re * cos(-mode*pi*((2.0*v*y)/N)) - // mode = 1 for forward, -1
f_im * sin(-mode*pi*((2.0*v*y)/N)); // for inverse transform
// sum_im permutated in the known way and + instead of -
在我看来,这种将内部cos和sin分组的值应该给出最低的舍入误差(与例如cos(-mode*2*pi*v*y/N)相比)
),因为不会将显著错误舍入的跨入pi相乘/相除几次,而只是一次。不是吗
比例因子1/M*N
或1/sqrt(M*N)
在最内层总和之外的每次分离后分别应用。里面更好吗?还是在两次分离结束时完全合并
为了进行更深入的分析,我退出了input->transform->save to file->read from file->transform^-1->output
工作流,并选择直接进行双精度比较:input->transform->transform^-1->output
这里是实际704x528 8 8位图像的结果(增量=输入和输出的实际部分之间的最大绝对差值):
- 输入在[0,1]内,不对称公式:δ=2.6609e-13(对应于[0255]范围内的6.785295e-11)
- 输入insde[0,1]和对称公式:delta=2.65232e-13(对应于[0255]范围的6.763416e-11)
- 输入在[0255]内,不对称公式:δ=6.74731e-11
- 输入在[0255]内,对称公式为:δ=6.7871e-11
然而,我很好奇:傅里叶变换最常用的实现是什么:对称还是非对称?输入[0,1]或[0255]通常使用哪个值范围?以及通常以对数刻度显示的光谱:例如,[0,1]输入的不对称变换后的[0,M*N]直接对数刻度为[0255],或线性刻度为[0255*M*N]之前的[0,M*N]?刻度会导致舍入误差。因此,解决方案1(缩放一次)优于解决方案2(缩放两次)。类似地,求和后缩放一次比求和前缩放所有对象要好 您是从
0
运行到2*N
还是从-N
运行到+N
?数学上是一样的,但在后一种情况下,你有额外的精度
顺便问一下,在
cos(-mode*stuff)
中mode
在做什么 您报告的错误很小,很正常,通常可以忽略。只需缩放结果并将目标间隔之外的任何结果钳制到端点即可
在FFT的库实现中(即编写供不同应用程序通用的FFT例程,而不是为单个应用程序定制的),很少考虑可伸缩性;该例程通常只返回由算术自然缩放的数据,而不使用额外的乘法运算来调整缩放。这是因为刻度通常与应用无关(例如,无论刻度是什么,都可以找到能量最大的频率),或者刻度可以通过乘法运算分布,并且只执行一次(例如,应用程序不需要在正变换和逆变换中进行缩放,而只需显式缩放一次即可获得相同的效果)。因此,由于通常不需要缩放,因此没有必要将其包含在库例程中
数据缩放到的目标间隔取决于应用程序
关于使用什么变换(对数或线性)来显示光谱的问题,我不能给出建议;我不使用可视化光谱。如果您使用相同的核心算法,并且只是缩放结果,则