Des nombres minuscules au lieu de zéro?

J’ai fait un cours masortingciel (en tant qu’exercice d’apprentissage) et j’ai rencontré des problèmes tout en testant ma fonction inverse.

Je saisis une masortingce arbitraire en tant que telle:

2 1 1 1 2 1 1 1 2 

Et j’ai réussi à calculer l’inverse et j’ai obtenu le bon résultat:

 0.75 -0.25 -0.25 -0.25 0.75 -0.25 -0.25 -0.25 0.75 

Mais quand j’ai essayé de multiplier les deux ensemble pour être sûr d’avoir la masortingce d’identité, je reçois:

 1 5.5111512e-017 0 0 1 0 -1.11022302e-0.16 0 1 

Pourquoi ai-je ces résultats? Je comprendrais si je multipliais des nombres étranges où je pouvais comprendre certaines erreurs d’arrondi, mais la sum que cela donne est la suivante:

 2 * -0.25 + 1 * 0.75 + 1 * -0.25 

qui est clairement 0, pas 5.111512e-017

Si je le fais manuellement pour faire le calcul; par exemple:

 std::cout << (2 * -0.25 + 1 * 0.75 + 1 * -0.25) << "\n"; 

Je reçois 0 comme prévu?

Tous les nombres sont représentés en double. Voici ma surcharge de multiplication:

 Masortingx operator*(const Masortingx& A, const Masortingx& B) { if(A.get_cols() == B.get_rows()) { Masortingx temp(A.get_rows(), B.get_cols()); for(unsigned m = 0; m < temp.get_rows(); ++m) { for(unsigned n = 0; n < temp.get_cols(); ++n) { for(unsigned i = 0; i < temp.get_cols(); ++i) { temp(m, n) += A(m, i) * B(i, n); } } } return temp; } throw std::runtime_error("Bad Matrix Multiplication"); } 

et les fonctions d’access:

 double& Masortingx::operator()(unsigned r, unsigned c) { return data[cols * r + c]; } double Masortingx::operator()(unsigned r, unsigned c) const { return data[cols * r + c]; } 

Voici la fonction pour trouver l’inverse:

 Masortingx Inverse(Masortingx& M) { if(M.rows != M.cols) { throw std::runtime_error("Masortingx is not square"); } int r = 0; int c = 0; Masortingx augment(M.rows, M.cols*2); augment.copy(M); for(r = 0; r < M.rows; ++r) { for(c = M.cols; c < M.cols * 2; ++c) { augment(r, c) = (r == (c - M.cols) ? 1.0 : 0.0); } } for(int R = 0; R < augment.rows; ++R) { double n = augment(R, R); for(c = 0; c < augment.cols; ++c) { augment(R, c) /= n; } for(r = 0; r < augment.rows; ++r) { if(r == R) { continue; } double a = augment(r, R); for(c = 0; c < augment.cols; ++c) { augment(r, c) -= a * augment(R, c); } } } Matrix inverse(M.rows, M.cols); for(r = 0; r < M.rows; ++r) { for(c = M.cols; c < M.cols * 2; ++c) { inverse(r, c - M.cols) = augment(r, c); } } return inverse; } 

Vous avez des nombres comme 0.250000000000000005 dans votre masortingce inversée, ils sont juste arrondis pour l’affichage afin que vous voyiez de beaux petits nombres ronds comme 0.25.

Veuillez lire ce document: Ce que tout informaticien devrait savoir sur l’arithmétique en virgule flottante

Vous ne devriez pas avoir de problèmes avec ces nombres, car avec cette masortingce particulière, l’inverse est tout pouvoir de 2 et peut être représenté avec précision. En général, les opérations sur les nombres en virgule flottante introduisent de petites erreurs qui peuvent s’accumuler et les résultats peuvent être surprenants.

Dans votre cas, je suis à peu près sûr que l’inverse est inexact et vous ne faites qu’afficher les premiers chiffres. C’est-à-dire que ce n’est pas exactement 0,25 (= 1/4), 0,75 (= 3/4), etc.

Vous allez toujours rencontrer de telles erreurs d’arrondi en virgule flottante, en particulier lorsque vous travaillez avec des nombres qui n’ont pas de représentation binary exacte (c’est-à-dire que vos nombres ne sont pas égaux à 2 ^ (N) ou 1 / (2 ^ N), où N est une valeur entière).

Cela dit, il existe un certain nombre de moyens pour augmenter la précision de vos résultats et vous pouvez effectuer une recherche google sur des algorithmes d’élimination gaussiens numériquement stables à l’aide de valeurs à virgule flottante à précision fixe.

Vous pouvez également, si vous êtes prêt à prendre des décisions rapides, incorporer une bibliothèque mathématique inifinite qui utilise des nombres rationnels, et si vous faites ce choix, évitez simplement d’utiliser des racines qui peuvent créer des nombres irrationnels. Un certain nombre de bibliothèques peuvent vous aider à utiliser des nombres rationnels, tels que GMP . Vous pouvez également créer vous-même une classe rational , mais il est relativement facile de surcharger les résultats de plusieurs opérations mathématiques si vous utilisez uniquement des valeurs 64 bits non signées ainsi qu’une variable indicateur de signe supplémentaire pour les composants de vos nombres rationnels. C’est là que GMP, avec ses objects de chaîne entière de longueur illimitée, est utile.

C’est juste une simple erreur de virgule flottante. Même les double sur les ordinateurs ne sont pas précis à 100%. Il n’est tout simplement pas possible de représenter avec précision à 100% un nombre décimal en base 10 en binary avec un nombre fini de bits.