Ошибка округления по Фурье - PullRequest
1 голос
/ 05 января 2011

Я возиться с преобразованиями Фурье.Теперь я создал класс, который выполняет реализацию 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})

alt text

Ну, это довольно хорошо,так как это те ценности, которые я ожидаю.Есть только одна вещь, которая меня смущает.И все это связано с округлением двойных чисел.

Прежде всего, почему первые два числа не совпадают (0,8660..443 8 ) против (0, 8660..443).И почему он не может рассчитать ноль, где вы ожидаете это.Я знаю, что 2.8E-15 довольно близок к нулю, но это не так.

Кто-нибудь знает, как происходят эти предельные ошибки и , если , я могу и хочу что-то с этим сделать

Может показаться, что нет реальной проблемы, потому что это просто небольшие ошибки.Однако, как вы справляетесь с этими ошибками округления, если вы, например, сравниваете 2 значения.

5,2 + 0i != 5,1961524 + i2.828107*10^-15

Cheers

Ответы [ 3 ]

6 голосов
/ 05 января 2011

Я думаю, что вы уже объяснили это себе - ограниченная точность означает ограниченную точность. Конец истории.

Если вы хотите очистить результаты, вы можете сделать свое собственное округление до более разумного числа значащих цифр - тогда ваши нули будут отображаться там, где вы хотите.

Чтобы ответить на вопрос, заданный вашим комментарием, не пытайтесь сравнивать числа с плавающей запятой напрямую - используйте диапазон:

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

FAQ comp.lang.c содержит множество вопросов и ответов о с плавающей запятой, которые вам, возможно, будет интересно прочитать.

4 голосов
/ 05 января 2011

От http://support.microsoft.com/kb/125056

Упор на шахте.

Существует множество ситуаций, в которых точность, округление и точность в вычислениях с плавающей точкой могут генерировать результаты, удивляющие программиста.,Необходимо соблюдать четыре общих правила:

  1. В вычислениях с одинарной и двойной точностью результат обычно не будет более точным, чем одинарная точность.Если требуется двойная точность, убедитесь, что все члены в расчете, включая константы, указаны с двойной точностью.

  2. Никогда не предполагайте, что простое числовое значение точно представлено в компьютере.Большинство значений с плавающей точкой не могут быть точно представлены как конечное двоичное значение.Например, .1 - это .0001100110011 ... в двоичном формате (повторяется всегда), поэтому его невозможно представить с полной точностью на компьютере с использованием двоичной арифметики, включающей все ПК.

  3. Никогда не предполагайте, что результат с точностью до последнего десятичного знака. Всегда есть небольшие различия между «истинным» ответом и тем, что можно вычислить с конечной точностью любого блока обработки с плавающей запятой.

  4. Никогда не сравнивайте два значения с плавающей запятой, чтобы увидеть, равны они или нет. Это является следствием правила 3. Почти всегда они будут маленькимиРазличия между числами, которые «должны» быть равны.Вместо этого всегда проверяйте, равны ли числа почти.Другими словами, проверьте, является ли разница между ними очень маленькой или незначительной.


Обратите внимание, что хотя я ссылался на документ Microsoft, это не проблема Windows.Это проблема с использованием двоичного кода, и он находится в самом процессоре.

И, как примечание второй стороны, я склонен использовать тип данных Decimal вместо double: см. Этот связанный вопрос SO: decimal vs double!- Какой я должен использовать и когда?

1 голос
/ 05 января 2011

В C # вы захотите использовать тип «десятичный», а не двойной для точности с десятичными точками.

Относительно «почему» ... представление дробей в разных базовых системах дает разные ответы. Например, 1/3 в системе Base 10 - это 0,33333 повторяющихся, но в системе Base 3 - 0,1.

Двойное значение - это двоичное значение на основе 2. При преобразовании в десятичное число с основанием 10 вы можете ожидать появления этих ошибок округления.

...