Странный результат умножения

В моем коде у меня есть это умножение в коде C++ со всеми типами переменных как double[]

f1[0] = (f1_rot[0] * xu[0]) + (f1_rot[1] * yu[0]); 
f1[1] = (f1_rot[0] * xu[1]) + (f1_rot[1] * yu[1]); 
f1[2] = (f1_rot[0] * xu[2]) + (f1_rot[1] * yu[2]); 

f2[0] = (f2_rot[0] * xu[0]) + (f2_rot[1] * yu[0]); 
f2[1] = (f2_rot[0] * xu[1]) + (f2_rot[1] * yu[1]);
f2[2] = (f2_rot[0] * xu[2]) + (f2_rot[1] * yu[2]);

соответствующие этим значениям

Force Rot1 : -5.39155e-07, -3.66312e-07
Force Rot2 : 4.04383e-07, -1.51852e-08

xu: 0.786857, 0.561981, 0.255018
yu: 0.534605, -0.82715, 0.173264

F1: -6.2007e-07, -4.61782e-16, -2.00963e-07
F2: 3.10073e-07, 2.39816e-07, 1.00494e-07

это умножение, в частности, дает неправильное значение -4,61782e-16 вместо 1,04745e-13.

f1[1] = (f1_rot[0] * xu[1]) + (f1_rot[1] * yu[1]);  

Я вручную проверил другие умножения на калькуляторе, и все они, похоже, дают правильные значения.

это открытый код, скомпилированный с помощью mpi, и приведенный выше результат предназначен для работы одного процессора, существуют разные значения при работе с несколькими процессорами, например, 40 процессоров дают 1,66967e-13 в результате умножения F1[1].

Это какая-то ошибка mpi? или проблема точности типа? и почему это работает нормально для других умножений?


person M.K    schedule 26.06.2014    source источник
comment
MPI намекает на состояние гонки: математика почти никогда не является ошибкой.   -  person IdeaHat    schedule 26.06.2014
comment
что такое f1_rot[0] и f1_rot[1]/   -  person user3344003    schedule 26.06.2014
comment
Да, я бы также сказал, что вы создали состояние гонки. Возможно, вы забыли какой-то барьер перед показанным кодом (f1_rot, xu и т. д. не были установлены надежно до выполнения умножения).   -  person Gugi    schedule 26.06.2014
comment
@ user3344003 Форсировать Rot1 и Форсировать Rot2   -  person M.K    schedule 26.06.2014
comment
Не удается воспроизвести.   -  person T.C.    schedule 26.06.2014
comment
@MadScienceDreams, гонки данных в парадигме, которая смоделирована на процессах, не разделяющих память, правда?   -  person Hristo Iliev    schedule 27.06.2014


Ответы (1)


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

f1[1] = (f1_rot[0] * xu[1]) + (f1_rot[1] * yu[1])
      = -3.0299486605499998e-07 + 3.0299497080000003e-07
      = 1.0474500005332475e-13

Это то, что мы получаем с числами, которые вы дали в своем примере. Обратите внимание, что (-7) - (-13) = 6 соответствует количеству десятичных знаков в поплавке, которое вы указали в своем примере: (например: -5.39155e-07 -3.66312e-07, каждая мантисса имеет точность 6 знаков после запятой). Это означает, что вы использовали здесь числа с плавающей запятой одинарной точности.

Я уверен, что в ваших расчетах точность ваших чисел больше, поэтому вы получаете более точный результат.

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

  • Простые точные поплавки: (a - b) / b >= ~1e-7
  • Поплавки двойной точности: (a - b) / b >= ~4e-16

Для получения дополнительной информации см. эти примеры... или таблицу в этой статье...

person Taha    schedule 26.06.2014
comment
Итак, да, теперь я понимаю, как я получаю эти числа. Однако это означает потерю точности при переходе на большее количество процессоров? как это происходит и можно ли это исправить? - person M.K; 26.06.2014
comment
Похоже, вы делаете матричный продукт. Для больших матриц суммируется более двух членов. Если вас интересуют коэффициенты, то теоретически можно использовать некоторые приемы уменьшения потери информации: например, суммирование отдельно отрицательных и положительных чисел от наименьшего (по абсолютной величине) к наибольшему. Существует также так называемое суммирование Кэхана..., которое может быть более адаптировано к матрицам. В любом случае, вы не будете делать это вручную, некоторые библиотеки прекрасно с этим справляются. - person Taha; 26.06.2014
comment
››› В целом, вам не потребуется большая точность для всех коэффициентов вашей матрицы; действительно, матрица — это набор множества значений, которые вместе имеют значение. Когда вы заметите потерю точности, это не коснется самых больших коэффициентов, это повлияет только на те, которые незначительны. Однако вы можете получить плохо обусловленную матрицу, если контраст между меньшими и большими коэффициентами высок. - person Taha; 26.06.2014
comment
Чтобы повысить точность ваших чисел, должны быть некоторые (символические) библиотеки, которые делают это очень хорошо. Однако вы можете заметить замедление в ваших вычислениях. Вы можете попробовать, например, GNU... - person Taha; 26.06.2014