2010-04-27 2 views
1

Кодирование умножения матрицы в моей программе, я получаю точность ошибок (неточные результаты для больших матриц).Прецизионная ошибка при умножении матрицы

Вот мой код. Текущий объект имеет данные, хранящиеся в сплющенном массиве, строка за строкой. Другая матрица B имеет данные, хранящиеся в сплющенном массиве, после столбца (поэтому я могу использовать арифметику указателей).

protected double[,] multiply (IMatrix B) 
{ 
    int columns = B.columns; 
    int rows = Rows; 
    int size = Columns; 

    double[,] result = new double[rows,columns]; 
    for (int row = 0; row < rows; row++) 
    { 
     for (int col = 0; col < columns; col++) 
     { 
      unsafe 
      { 
       fixed (float* ptrThis = data) 
       fixed (float* ptrB = B.Data) 
       { 
        float* mePtr = ptrThis + row*rows; 
        float* bPtr = ptrB + col*columns; 
        double value = 0.0; 
        for (int i = 0; i < size; i++) 
        { 
         value += *(mePtr++) * *(bPtr++); 
        } 
        result[row, col] = value; 
       } 
      } 
     } 
    } 
} 

На самом деле, код немного сложнее: я делаю умножение вещи на несколько кусков (так вместо того, чтобы я от 0 до размера, я иду от localStart к localStop), а затем суммировать полученные матрицы ,

Моей проблема: для большой матрицы я получаю ошибку точности:

NUnit.Framework.AssertionException: Error at (0,1) 
    expected: <6.4209571409444209E+18> 
    but was: <6.4207619776304906E+18> 

Любой идею?

+0

Возможно, использование тега 'precision' должно автоматически открыть эту веб-страницу о математике с плавающей запятой, которая часто возникает в этих вопросах? – Skizz

ответ

0

Хем, это не решает проблему, но в NUnit, вы можете позволить иметь точность ошибку и выбрать значение этого эпсилон

0

В качестве отправной точки, использовать double везде вместо float.

+0

Мои массивы уже плавают [], ничего не могут с этим поделать. Проблемы с памятью в какой-то момент, все приложение с использованием float [] и т. Д. – Wam

+0

Тогда я не уверен, что вы можете многое сделать. Вы выбрасывали точность; нет никакой умности, которая вернет его. –

1

Я изначально заявил, что вы должны преобразовать floats в doubles. Однако, как вы указываете, это нарушит ваш алгоритм.

Вы можете попробовать:

value += (double)*(mePtr++) * (double)*(bPtr++); 

проблема с вашим кодом, как это сейчас стоит в том, что умножение делается в float точности затем добавляют к double. Литье до double сначала поможет в некоторой степени.

Возможно, было бы более понятным использовать промежуточные переменные double - но это зависит от вас.

Если это не дает вам точной точности, вам необходимо будет использовать decimal вместо double. Однако это может привести к поражению производительности, поэтому сначала выполните некоторые тесты.

+0

Да, но преобразование mePtr и bPtr в двойное, могу ли я использовать mePtr ++ или это будет использовать только половину элементов моего массива? b.data is float [], если mePtr является двойным указателем на b.data [0], не будет mePtr ++ быть 8 байтами справа, поэтому b.data [2] вместо b.data [1]? – Wam

+0

@Wam - В этом случае вам придется указывать значения в два раза в расчете - я обновлю ответ – ChrisF

+0

Пробовал, он не решает мою проблему ... – Wam

0

По крайней мере, вы должны использовать дуплеки повсюду. Поплавки очень неточны.

+0

Ну, поплавки менее точны, чем в двухместных , Это уж точно. Но все (ну, примерно все, моя статистика неточна) числа с плавающей запятой являются неточными. –

+0

@ Высокие, удваиваются еще 29 бит точности, чем поплавки. Это делает их примерно в 100 миллионов раз более точными. –

+1

@Marcelo - уверен. И все же многие из нас, делающие серьезный хруст, должны беспокоиться о потере точности при длительных вычислениях. Изменение плавающих чисел в два раза только откладывает проблему, и она не удаляет ее. Конечно, иногда он откладывает проблему до тех пор, пока вычисление не закончится. –

1

Возможно, все, что вам нужно сделать, это использовать Kahan summation. Но вы можете never expect получить точно конкретный результат с математикой с плавающей запятой.

0

Это феномен, называемый «Matrix Creep», который происходит постепенно во время манипуляций с матрицами, если вы не последовательно нормализуете свои матрицы.

1

Оказывается, это было просто ... ошибка. Законченный, что вместо того, чтобы:

float* mePtr = ptrThis + row*rows; 
float* bPtr = ptrB + col*columns; 

Правильные индексаторах для моих строк были:

float* mePtr = ptrThis + row * size; 
float* bPtr = ptrB + col * size; 

Извините за что, на самом деле не фантазии здесь ответ. Но спасибо за помощь!

Смежные вопросы