【问题标题】:Fourier transform rounding error傅立叶变换舍入误差
【发布时间】:2011-01-04 21:05:37
【问题描述】:

我在搞乱傅立叶变换。现在我创建了一个实现 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

干杯

【问题讨论】:

  • 确实如此,但是,如果我想比较这些值,它会说它们不一样。您将如何处理。
  • 阅读此内容:docs.sun.com/source/806-3568/ncg_goldberg.html 并在编写另一行代码之前了解所有内容。
  • @Eric,我已经阅读了介绍并快速阅读了它,它似乎很好地解释了我想要理解的内容。它正在打印 atm,所以今晚我有一个很好的阅读 xD

标签: c# floating-point double fft


【解决方案1】:

我想您已经对自己进行了解释 - 有限的精度意味着有限的精度。故事结束。

如果你想清理结果,你可以自己做一些四舍五入到更合理的有效数字 - 然后你的零会出现在你想要的地方。

要回答您的评论提出的问题,请不要尝试直接比较浮点数 - 使用范围:

if (Math.Abs(float1 - float2) < 0.001) {
  // they're the same!
}

comp.lang.c FAQ 有很多questions & answers about floating point,您可能有兴趣阅读。

【讨论】:

  • +1 表示范围的想法。当使用双精度数和其他显示精度损失的数字类型时,这几乎是您能做的最好的事情。
  • 请注意,这种事情可能会导致其自身的问题。例如,假设有 3 个,float1、float2 和 float3。您很容易遇到这样一种情况:您的代码报告 float1 与 float2“相同”,而 float2 与 float3“相同”,但 float1 与 float3 不同。这是一个奇怪的情况。许多算法(例如排序算法)都假设相等的事物彼此相等;当你想出不是等价关系的“平等”实现时要格外小心。
【解决方案2】:

来自http://support.microsoft.com/kb/125056

强调我的。

在许多情况下,浮点计算中的精度、舍入和准确度可以产生令程序员感到惊讶的结果。应该遵循四个一般规则:

  1. 在涉及单精度和双精度的计算中,结果通常不会比单精度更准确。如果需要双精度,请确保计算中的所有项(包括常量)都以双精度指定。

  2. 永远不要假设简单的数值可以在计算机中准确表示。大多数浮点值不能精确地表示为有限的二进制值。例如 .1 是 .0001100110011... 二进制(它永远重复),因此它不能在使用二进制算术的计算机上完全准确地表示,包括所有 PC。

  3. 永远不要假设结果精确到小数点后一位。“真实”答案与任何浮点的有限精度计算结果之间总是存在细微差别处理单元。

  4. 永远不要比较两个浮点值来判断它们是否相等。这是规则 3 的推论。数字之间几乎总是存在微小差异“应该”是平等的。相反,请始终检查数字是否几乎相等。换句话说,检查它们之间的差异是否非常小或微不足道。


请注意,虽然我引用了一个 microsoft 文档,但这不是 windows 问题。这是使用二进制的问题,并且在 CPU 本身中。

另外,我倾向于使用 Decimal 数据类型而不是 double:请参阅这个相关的 SO 问题:decimal vs double! - Which one should I use and when?

【讨论】:

  • 太棒了,我会用它。但是,我不能使用十进制数据类型,因为我不能用它进行 System.Math 和 System.Numerics.Complex 计算。
  • @Timo:一个好的经验法则是始终使用小数进行财务计算。对于科学计算,使用 double 并学会忍受小误差。
【解决方案3】:

在 C# 中,您需要使用“小数”类型,而不是 double 以确保小数点的准确性。

至于“为什么”...在不同的基本系统中表示分数会给出不同的答案。例如,以 10 为基数的系统中的 1/3 是 0.33333 循环,但在以 3 为基数的系统中是 0.1。

双精度值是二进制值,以 2 为底。转换为以 10 为底的十进制时,您可能会遇到这些舍入错误。

【讨论】:

  • 之所以选择 double 类型,是因为整个 Math 库和 System.Numerics(复数类型)都使用 double 来计算这些东西。我找不到可以使用双精度的库,所以我想如果我想使用小数,我必须自己写一个。
  • @Timo Willemsen:另一个可能的策略是将双打的精度设置在可接受的水平。基本上,决定什么时候四舍五入对你有用。
  • 小数也不是无限精度;出于与您所说的完全相同的原因,它们也会出现舍入误差。你也不能精确地用十进制表示 1/3。这也将被四舍五入,并且四舍五入误差将同样产生。在十进制中产生错误的值和操作的集合与双精度的集合不同,但它仍然是
  • @Joan:是的。您可以将分数表示为 两个 BigIntegers,一个用于分子,一个用于分母。使用这种技术,您可以完美地表示任何分数;当然,使用这种方案,你不能准确地表示像 pi 或 2 的平方根这样的无理数。
  • @Joan:不客气。让学生学习一门语言的一个相当常见的练习是实现一个“有理”类型,该类型可以对实现为一对整数的有理数进行所有算术运算。这也是基本数论的一个很好的复习。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多