傅立叶变换舍入误差
我正在搞乱傅里叶变换。现在我创建了一个类来执行 DFT 的实现(不执行类似 FFT atm 的任何操作)。这是我使用过的实现:
public static Complex[] Dft(double[] data)
{
int length = data.Length;
Complex[] result = new Complex[length];
for (int k = 1; k <= length; k++)
{
Complex c = Complex.Zero;
for (int n = 1; n <= length; n++)
{
c += Complex.FromPolarCoordinates(data[n-1], (-2 * Math.PI * n * k) / length);
}
result[k-1] = 1 / Math.Sqrt(length) * c;
}
return result;
}
这些是我从 Dft({2,3,4})
嗯,看起来还不错,因为这些是我期望的值。只有一件事让我感到困惑。这一切都与双打的四舍五入有关。
首先,为什么前两个数字不完全相同 (0,8660..443 8 ) 和 (0,8660..443)。为什么它不能计算出您期望的零。我知道 2.8E-15 非常接近于零,但事实并非如此。
任何人都知道这些边际错误是如何发生的,以及我是否可以并且想要对此采取措施。
看起来似乎没有真正的问题,因为这只是一些小错误。但是,如果您要比较 2 个值,如何处理这些舍入误差。
5,2 + 0i != 5,1961524 + i2.828107*10^-15
干杯
I'm messing around with Fourier transformations. Now I've created a class that does an implementation of the DFT (not doing anything like FFT atm). This is the implementation I've used:
public static Complex[] Dft(double[] data)
{
int length = data.Length;
Complex[] result = new Complex[length];
for (int k = 1; k <= length; k++)
{
Complex c = Complex.Zero;
for (int n = 1; n <= length; n++)
{
c += Complex.FromPolarCoordinates(data[n-1], (-2 * Math.PI * n * k) / length);
}
result[k-1] = 1 / Math.Sqrt(length) * c;
}
return result;
}
And these are the results I get from Dft({2,3,4})
Well it seems pretty okay, since those are the values I expect. There is only one thing I find confusing. And it all has to do with the rounding of doubles.
First of all, why are the first two numbers not exactly the same (0,8660..443 8 ) vs (0,8660..443). And why can't it calculate a zero, where you'd expect it. I know 2.8E-15 is pretty close to zero, but well it's not.
Anyone know how these, marginal, errors occur and if I can and want to do something about it.
It might seem that there's not a real problem, because it's just small errors. However, how do you deal with these rounding errors if you're for example comparing 2 values.
5,2 + 0i != 5,1961524 + i2.828107*10^-15
Cheers
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我想你已经向自己解释过了——有限的精度意味着有限的精度。故事结束。
如果您想清理结果,您可以自己进行一些四舍五入到更合理的有效位数 - 然后您的零将显示在您想要的位置。
要回答您的评论提出的问题,不要尝试直接比较浮点数 - 使用范围:
comp.lang .c FAQ 有很多问题和问题。关于浮点的答案,您可能有兴趣阅读。
I think you've already explained it to yourself - limited precision means limited precision. End of story.
If you want to clean up the results, you can do some rounding of your own to a more reasonable number of siginificant digits - then your zeros will show up where you want them.
To answer the question raised by your comment, don't try to compare floating point numbers directly - use a range:
The comp.lang.c FAQ has a lot of questions & answers about floating point, which you might be interested in reading.
来自 http://support.microsoft.com/kb/125056
强调我的。
在许多情况下,浮点计算中的精度、舍入和准确度可以产生令程序员感到惊讶的结果。应遵循四个一般规则:
在同时涉及单精度和双精度的计算中,结果通常不会比单精度更准确。如果需要双精度,请确保计算中的所有项(包括常量)均以双精度指定。
永远不要假设一个简单的数值可以在计算机中准确表示。大多数浮点值无法精确表示为有限的二进制值。例如,.1 在二进制中是 .0001100110011...(它会永远重复),因此它无法在使用二进制算术的计算机(包括所有 PC)上完全准确地表示。
永远不要假设结果精确到小数点后一位。“真实”答案与任何浮点处理单元的有限精度计算得出的结果之间总是存在微小差异。
永远不要比较两个浮点值来查看它们是否相等或不相等。这是规则 3 的推论。数字之间几乎总是存在“应该”的微小差异。 “平等。相反,请始终检查数字是否几乎相等。换句话说,检查它们之间的差异是否非常小或不显着。
请注意,虽然我引用了微软文档,但这不是Windows问题。这是使用二进制的问题,并且是 CPU 本身的问题。
而且,作为第二个旁注,我倾向于使用 Decimal 数据类型而不是 double:请参阅此相关的 SO 问题: 十进制与双精度! - 我应该使用哪一个以及何时使用?
From http://support.microsoft.com/kb/125056
Emphasis mine.
There are many situations in which precision, rounding, and accuracy in floating-point calculations can work to generate results that are surprising to the programmer. There are four general rules that should be followed:
In a calculation involving both single and double precision, the result will not usually be any more accurate than single precision. If double precision is required, be certain all terms in the calculation, including constants, are specified in double precision.
Never assume that a simple numeric value is accurately represented in the computer. Most floating-point values can't be precisely represented as a finite binary value. For example .1 is .0001100110011... in binary (it repeats forever), so it can't be represented with complete accuracy on a computer using binary arithmetic, which includes all PCs.
Never assume that the result is accurate to the last decimal place. There are always small differences between the "true" answer and what can be calculated with the finite precision of any floating point processing unit.
Never compare two floating-point values to see if they are equal or not- equal. This is a corollary to rule 3. There are almost always going to be small differences between numbers that "should" be equal. Instead, always check to see if the numbers are nearly equal. In other words, check to see if the difference between them is very small or insignificant.
Note that although I referenced a microsoft document, this is not a windows problem. It's a problem with using binary and is in the CPU itself.
And, as a second side note, I tend to use the Decimal datatype instead of double: See this related SO question: decimal vs double! - Which one should I use and when?
在 C# 中,为了保证小数点的准确性,您需要使用“decimal”类型,而不是 double。
至于“为什么”……不同基础系统中的代表分数给出了不同的答案。例如,1/3 在以 10 为基数的系统中是 0.33333,但在以 3 为基数的系统中是 0.1。
double 是一个二进制值,以 2 为基数。当转换为以 10 为基数的十进制时,您可能会出现这些舍入误差。
In C# you'll want to use the 'decimal' type, not double for accuracy with decimal points.
As to the 'why'... repsensenting fractions in different base systems gives different answers. For example 1/3 in a base 10 system is 0.33333 recurring, but in a base 3 system is 0.1.
The double is a binary value, at base 2. When converting to base 10 decimal you can expect to have these rounding errors.