Seltsames verhalten von (long) double beim * und *= Operator



  • Hallo, in meiner Matrixklasse habe ich zum Multiplizieren den * und *= Operator eingerichtet. Dabei gibt der * Operator die richtigen Werte aus, wohingegen der *= Operator extrem ungenau ist. zB. bei der Multiplikation der (long double) Matrix

    0.5 0.3 0.6
    0.2 0.2 0.1
    0.3 0.5 0.3
    

    mit sich selbst, erhalte ich für das Element [0][0] der ErgebnisMatrix folgende werte:

    Erwarteter Wert:         0.49
    Von * errechneter Wert:  0.489999999999999999 //O.K.
    Von *= errechneter Wert: 0.500000000000000000 //Abweichung von
                                                  //0.1
    

    Die von *= ermittelten Werte weichen im Schnitt um 0.1 bis 0.2 vom richtigen Wert ab (was ja um ein vielfaches über dem zu erwartenden Rundungsfehler liegt). Dabei habe ich beide Opertoren (ungefähr) gleich implementiert (der Rechenweg kann also nicht Schuld sein):

    //* Operator
    template <typename T> Matrix<T> Matrix<T>::operator*(Matrix const& obj) const
    {
        Matrix tmp(*this); //erstellt neues 2D array, das die werte von this übernimmt
    
        Matrix Ergebnis(tmp.Y , obj.X); //erstellt X*Y Array, das mit 0 initialisiert ist
    
        for(size_t x=0;x!=Ergebnis.X;++x)
            for(size_t y=0;y!=Ergebnis.Y;++y) //spaltenweise durchgehen
                for(size_t a=0;a!=tmp.X;++a)
                     Ergebnis.pointer[x][y]+=(tmp.pointer[a][y]*obj.pointer[x][a]);
        return Ergebnis;
    }
    
    //*= Operator
    template <typename T> Matrix<T>& Matrix<T>::operator*=(Matrix const& obj)
    {
        Matrix tmp(*this);
    
        for(size_t x=0;x!=X;++x)
        {
            for(size_t y=0;y!=Y;++y)
            {
                pointer[x][y]=0;
                for(size_t a=0;a!=X;++a)
                {
                    pointer[x][y]+=(tmp.pointer[a][y]*obj.pointer[x][a]);
                }
            }
        }
        return (*this);
    }
    

    Sieht jemand, woran das liegen könnte 😕



  • Geh am besten mit einem Debugger durch oder bau Ausgaben bei jedem Zwischenschritt ein. So kannst du beide Algorithmen 1:1 vergleichen.

    Deine Vorgehensweise ist aber ziemlich inkonsistent. Warum implementierst du den operator* nicht über den operator*= ? Dann fällt auch die Codeduplizierung weg.



  • Jetzt versteh ich garnichts mehr. Ich habe jetzt den * Operator über *= implementiert.

    template <typename T> Matrix<T> Matrix<T>::operator*(Matrix const& obj) const
    {
        Matrix tmp(*this);
        return tmp*=obj;
    }
    

    Trotzdem sind die Ergebnisse, die der * Operator liefert, wieder richtig, wohingegen die Ergebnisse des *= Operator die starke Abweichung aufweisen Oo



  • Das kann nicht sein. Hast du die richtigen Objekte geprüft? Und hast du sie richtig geprüft?

    Ansonsten wird es wohl Zeit für den Debugger...



  • Wie sieht Dein Kopierkonstrukor aus? Kopierst Du die Matrix tatsächlich?
    Dein Member pointer klingt so als wäre es er ein Pointer.

    DJohn



  • was meinst du mit Objekte geprüft?
    Also von der Implementierung her sehe ich keine Fehler

    template <typename T> class Matrix
    {
       private:
        size_t X;
        size_t Y;
        T** pointer;
    
       public:
        Matrix(const Matrix& obj);
        Matrix operator * (Matrix const& obj) const;
        Matrix& operator*=(Matrix const& obj);
        ~Matrix();
    };
    
    template <typename T> Matrix<T>& Matrix<T>::operator*=(Matrix const& obj)
    {
        Matrix tmp(*this);
        for(size_t x=0;x!=X;++x)
        {
            for(size_t y=0;y!=Y;++y)
            {
                pointer[x][y]=0;
                for(size_t a=0;a!=X;++a)
                {
                    pointer[x][y]+=(tmp.pointer[a][y]*obj.pointer[x][a]);
                }
            }
        }
        return (*this);
    }
    
    template <typename T> Matrix<T> Matrix<T>::operator*(Matrix const& obj) const
    {
        Matrix tmp(*this);
        return tmp*=obj;
    }
    
    template <typename T> Matrix<T>::Matrix(const Matrix& obj)
    {
        X=obj.X;
        Y=obj.Y;
    
        pointer = new T*[X];
        for(size_t i=0;i!=X;++i)
            pointer[i] = new T[Y];
    
        for(size_t y=0;y!=Y;++y)
            for(size_t x=0;x!=X;++x)
                pointer[x][y]=obj.pointer[x][y];
    }
    
    template <typename T> Matrix<T>::~Matrix()
    {
        for(size_t i=0;i!=X;++i)
            delete [] pointer[i];
        delete [] pointer;
    }
    


  • Und der Zuweisungsoperator? Und wie initialisierst du die Werte?



  • template <typename T> Matrix<T>& Matrix<T>::operator = (const Matrix& obj)
    {
        for(size_t i=0;i!=X;++i)
            delete [] pointer[i];
        delete [] pointer;
    
        X=obj.X;
        Y=obj.Y;
    
        pointer = new T*[X]; //zeiger auf array aus zeigern
        for(size_t i=0;i!=X;++i)
            pointer[i] = new T[Y];
    
        for(size_t y=0;y!=Y;++y)
            for(size_t x=0;x!=X;++x)
                pointer[x][y]=obj.pointer[x][y];
    
        return (*this);
    }
    


  • Es scheint mir merkwürdig, dass die Werte nach beiden Operationen nur knapp voneinander abweichen. Bei einer fehlerhaften Zuweisung sieht es meist schlimmer aus...

    Mtrx, du willst dich wohl aus Prinzip nicht mit dem Debugger anfreunden? Ich würds dir aber raten. Der kann dir in Zukunft sehr, sehr hilfreich sein.



  • Bin halt noch neu bei C++ und hab noch nie mit dem Debugger gearbeitet. Aber wird wohl Zeit, dass ich das auch mal lerne ^^



  • Wirklich. Du wirst den Debugger schon noch zu schätzen lernen. 😉

    Aber für den Anfang reichen auch Konsolenausgaben. Gib die Zwischenresultate mit std::cout aus und schau, ab wo Abweichungen auftreten.



  • Schande über mein Haupt! Der "Fehler" hatte nichts mit dem Quellcode zu tun sondern mit einer falschen Zuweisung im main() part Oo Jetzt funktioniert alles wunderbar


Anmelden zum Antworten