Задать вопрос
AlexeevVyacheslav
@AlexeevVyacheslav
Веб-разработчик из Иркутска

Как справиться с неправильным нулем в C++?

Добрый день!
Работаю с матрицами, сейчас возникла такая проблема.
Есть единичная матрица 3х3
Bsig:
1 0 0
0 1 0
0 0 1

По обычному алгоритму нахожу обратную (в результате должна получиться такая же единичная матрица)
Bm1sig:
1 -0 0
-0 1 -0
0 -0 1

И, как я считаю, из-за этих минус нолей перемножение этой матрицы с матрицей-столбцом b:
3
1
1

Получается всегда разный результат:
Bm1sig * b = (3, 0, 6.9397e-310)
Bm1sig * b = (3, 1.49167e-154, 1)
Bm1sig * b = (3, -1.49167e-154, 1)
(Матрица строка, 3 разных варианта)

Еще при нахождении обратной матрицы иногда проскакивают такие вещи:
-1.5 + 0.3 + 1.2 = 2.22045e-16

Подскажите, как с этим бороться?
Алгоритм обратной матрицы:
(Создан класс Matrix с двумя переменными m*n и матрицей **double)
	Matrix Inverse() {
		Matrix result(n,m);

		for(int i=0; i<n; i++){
			for(int j=0; j<m; j++){
				result[i][j] =  this->PreMinor(i,j).Determinant() * ((i+j)%2 ? -1 : 1) * (1/this->Determinant());
			}
		}

		result = result.Transpose();

		return result;
	}

	double Determinant() {
		if(n == 1 && m == 1)
			return array[0][0];

		double D = 0;
		for(int i=0; i<n; i++){
			D += array[0][i] * (i%2 ? -1 : 1) * this->PreMinor(0,i).Determinant();
		}

		return D;
	}

	Matrix PreMinor(int x, int y) {
		Matrix result(m-1,n-1);

		for(int i=0, in=0; i<n; i++) 
			if(i != x){
				for(int j=0, jn=0; j<m; j++){
					if(j != y)
						result[in][jn++] = array[i][j]; // можно array[i][j];
				}
				in++;
			}

		return result;
	}
  • Вопрос задан
  • 4177 просмотров
Подписаться 2 Оценить Комментировать
Решения вопроса 1
Для все простых типов в C++ есть верхние и нижние пределы, а также некоторая эпсилон окрестности, которые лежат в numeric_limits. В Вашем случае, если значение будет меньше эпсилон, можно считать числа равными. Например, сравнение вещественных переменных а и b будет выглядеть так:
bool isSame( double a, double b)
{
return  std::fabs(a - b ) <= std::numeric_limits<double>::epsilon()
}

Если isSame истина, значит, числа равны, иначе нет.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 3
gbg
@gbg Куратор тега C++
Любые ответы на любые вопросы
Это нормальное поведение арифметики с плавающей запятой, а три на десять в минус сто пятьдесят четвертой степени - практически ноль. Если желаете работать в рациональных числах, используйте GMP
Ответ написан
Комментировать
SHVV
@SHVV
Это нормально. Вы просто упёрлись в ограничение разрядной сетки чисел с плавающей запятой. В реальных приложениях нужно учится с этим жить.

Например, для вывода пользователю, всегда округлять до некоторого знака в зависимости от величин, тогда числа, близкие к нулую, станут просто нулями.
При сравнении чисел между собой необходимо задавать порог схожести опять же в зависимости от порядка чисел и их точности.
Операции с матрицами, которые требуют соблюдения ортонормирования, дополнять принудительным ортонормированием.
И т. д.
Ответ написан
@Koss1024
Это https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D1%88%D...

Нужно понимать принципы работы чисел с плавающей точкой если вы работаете с математикой
Именно поэтому такие числа напрямую не сравнивают, а сравнивают разность по модулю с eps
if(abs(a-b) < 1e-10) // то числа равны число не совсем от балды
при обращении матриц порядок погрешности можно вычислить для маленьких и хорошо обусловленных матриц это весьма точно. Но чаще это число куда меньше

Следующая проблема которая у Вас возникнет плохое обращение
Читайте про число обусловленности матрицы

Кстати говоря обратную матрицу так искать дело неблагодарное :)
Численные методы алгебры вам в помощь

Самый простой для понимания LU разложение и его PLU модификация
Практически применяют разные для разных задач. Как правило о матрице что-то известно заранее
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы