vector<complex <double> > umwandeln in double*



  • Hallo Leute

    Irgendwie wurde ich nach dem ersten Eintrag nicht mehr per e-mail benachrichtigt deswegen antworte ich leider erst jetzt. Sorry deswegen.

    Also bei meinem Problem ist die Anordnung der Real und Imaginärteile total egal, da die Routine der NAG-Bibliothek sowieso nur mit real-Teilen arbeitet, allerdings kann ich ihr durch einen kleinen Trick vorgaukeln das sie Reele-Zahlen hat.

    Naja habe in der Zwischenzeit mein Problem selber gelöst:

    int main ()
    {
            vector< complex <double> > t;
            t.push_back(complex<double> (1.,2.5));
            t.push_back(complex<double> (4.5,6.7));
            double *d;
    
            d = reinterpret_cast<double*> (&t[0] );
    
            for(unsigned int count = 0;count < 4<;count++)
            {
                    cout << d[count] << endl;
                    cout.flush();
            }
    
            return (0);
    }
    

    Die ausgabe ist:
    duwan@duwan-laptop:~/Documents/c/Diplom-Arbeit/test$ ./exe
    1
    2.5
    4.5
    6.7

    Die Frage die sich jetzt stellt ist die folgende: Ist der Speicher eines Vectors zusammenhängend? Denn wenn nicht kann ich bei dieser Herrangehensweise bei größeren Vectoren ziemliche Probleme kriegen.
    Lese dazu gerade im Stroustrup aber habe dazu leider noch keine genau Aussage aber einige Hinweise dahingegen gefunden dass der Speicherbereich in der Tat zusammenhängend ist.



  • japp, vector ist ein einziges "stück speicher"...

    edit:

    aber warum so umständlich?

    typedef std::vector <complex <float> > TCXVector;
    TCXVector CXVector; //kA, ob das stimmt ^^
    //füllen
    
    for (TCXVector::const_iterator iter(CXVector.begin()), end (CXVector.end()); iter != end; ++iter)
    {
     std::cout << iter->Real() << std::endl;
    }
    


  • duwan schrieb:

    Die Frage die sich jetzt stellt ist die folgende: Ist der Speicher eines Vectors zusammenhängend?

    Ja, ist er.
    Aber dennoch ist dein Code ein wenig russisches Roulette: Es ist nirgendwo garantiert, dass ein complex<double> auch wirklich nur aus zwei doubles besteht (das ist Implementationssache). Daher ist der reinterpret_cast eine recht unsichere Sache.

    Wenn du eh nur die Realteile haben möchtest ist die Sache natürlich deutlich einfacher:

    std::vector<std::complex<double> > cplxvec;
    //fuellen
    std::vector<double> doubles;
    std::transform(cplxvec.begin(), cplxvec.end(), std::back_inserter(doubles), std::mem_fun(&std::complex<double>::real));
    


  • Habe mir gerade mal alle Beiträge überflogen:

    Also habe mich in der Zwischenzeit mal mit einem Studienkollegen unterhalten, und er sagte, dass es nicht sinnvoll ist eine abgeleitete Klasse zu schrieben, da die ganzen Operatoren complex-Variablen erwarten. Das bedeutet das man halt alle Operatoren neu bzw. umschreiben müsste.

    @Tachyon
    Bin mir nicht sicher wie du das meinst. Wenn ich dich richtig verstanden habe dann willst du den Inhalt des vectors in dein Array kopieren. Das kommt bei mir aber nicht in Frage, da die Umwandlung wahrscheinlich SEHR häufig durchgeführt werden müsste und der vector so bis 10000 complexe Zahlen enthalten soll.

    @pumuckl,Fellhuhn
    Äh danke für eure Antwort allerdings verstehe ich euren Code leider nicht.



  • @pumuckl
    Sorry da habe ich mich wohl etwas unverständlich ausgedrück. Brauche beide Teile allerdings gaugkle ich der NAG-Routine vor es mit einem reelen Array doppelter Länge zu tun zu haben. Also werden die Imaginär-Teile einfach als zusätzliche Dimension in der NAG-Routine behandelt.



  • wie wärs damit: du implementierst es genau so, wie es richtig wäre und am saubersten ist (mit array umkopieren etc) und guckst dann mit nem profiler, wie viel das stück code denn nun ausmacht - da das rechnen vermutlich wesentlich komplexer ist, wird das umkopieren nicht wirklich ins gewicht fallen - falls es dann iwo viel zu lange dauert, musst du das dann halt ein wenig abändern - aber wie in letzter zeit sehr häufig in diesem forum geschrieben wurde: vorzeitige optimierung ist sinnlos - was nützt dir (falls es extrem viel ist) ne 10tel sekunde beim umkopieren, wenn du für`s rechnen eh ein vielfaches dieser zeit benötigst?



  • @pumuckl
    Okay glaube ich weiß was du meinst. Allerdings ist das in meinem Programm schon berügsichtigt, da ich den reinterpret_cast<double*> (&t[0] ) genommen habe. reinterpret_cast<double*> (&t ) funktioniert nicht. Das habe ich als erstes ausprobier. Offensichtlich scheint intern die Variablen DIRECT aneinander zu hängen, so dass wenn man einmal den Anfang richtig gefunden hat ....
    Trotztdem bekomme ich natürlich Bödsinn wenn ich die Schleife mehr als 4 mal durchlaufen lasse.

    Auch muss ich das ganze in einen echten double-Array umwandeln. Und soweit ich weis kann der nicht mit Iteratoren umgehen. Allerdings habe ich mich noch nie so richtig mit Iteratoren befasst 🙄



  • @unskilled
    Im Prinzip hast du recht, allerdings geht es mir auch darum eleganten und guten Code abzuliefer und noch was dabei zu lehrnen damit ich es in zukunft besser und schneller programmieren kann. Auch führt eleganter Code zu weniger Programmierfehlern die am Ende, wenn man erhlich ist, doch am meisten Zeit kosten



  • duwan schrieb:

    ...Auch muss ich das ganze in einen echten double-Array umwandeln...

    Das war mir eigentlich schon von vorne herein klar - Typuminterpretationen bringen nur dann etwas wenn man wirklich 100%ig sicher ist wie diese Typen sich im Speicher verhalten. Alles Andere als umkopieren ist hier, wenn überhaupt nur mit sehr viel Glück (und unportabel) möglich.

    cu André



  • Habe mal ein kleines Programm geschrieben um meine Idee auf Fehler zu überprüfen:

    vector< complex <double> > t;
            t.clear();
            double *d;
    
            //Füllen
    
            for(unsigned int count = 0; count < 10000000 ; count ++)
            {
                    t.push_back(complex<double>(count,count));
            }
    
            d = reinterpret_cast<double*> (&t[0] );
    
            for(unsigned int count = 0;count < t.size(); count ++)
            {
                    if(t[count] != complex<double>(d[count*2],d[count*2 +1]))
                    {
                            break;
                            cout << "Fehler" << endl;
                    }
            }
    
            cout << "Ende" << endl;
    

    Läuft bei mir ohne Probleme durch. Ich glaube ich bin auf der Sicheren Seite
    Danke für eure Hilfe.

    @asc
    Da nach den Aussagen der Anderen scheint mir der Speicher des Vectors innerhalb des c++ Standard zusammenhängend zu sein, und das ist das einzige was ih hier benutzte. Ich denke das solle daher keine Probleme mit der Portierung machen. Ich habe unter Kubuntu den g++ benutzt. Wenn du einen anderen Compiler benutzt kannst du das ganze ja auch mal bei dir versuchen. Das Ergebn is würde ich wirklich interresieren.



  • duwan schrieb:

    @pumuckl
    Okay glaube ich weiß was du meinst. Allerdings ist das in meinem Programm schon berügsichtigt, da ich den reinterpret_cast<double*> (&t[0] ) genommen habe. reinterpret_cast<double*> (&t ) funktioniert nicht. Das habe ich als erstes ausprobier. Offensichtlich scheint intern die Variablen DIRECT aneinander zu hängen, so dass wenn man einmal den Anfang richtig gefunden hat ....
    Trotztdem bekomme ich natürlich Bödsinn wenn ich die Schleife mehr als 4 mal durchlaufen lasse.

    Auch muss ich das ganze in einen echten double-Array umwandeln. Und soweit ich weis kann der nicht mit Iteratoren umgehen. Allerdings habe ich mich noch nie so richtig mit Iteratoren befasst 🙄

    Dass das mit dem reinterpret_cast<double*> (&t ) nicht funktioniert ist klar, dabei nimmst du die Adresse des vector-Objekts und nicht des Datenarrays das vom vector verwaltet wird (im Grunde verwaltet ein std::vector intern nur ein dynamisches Array).
    Was ich aber meinte ist was anderes: Mit dem reinterpret_cast gehst du davon aus, dass ein complex im Speicher aus genau zwei doubles besteht und nichts weiter. Das ist aber nicht garantiert. Es könnten z.B. irgendwelche zusätzliche Elemente vorhanden sein (z.B. zu Debug-Zwecken), dann gibts Dinge wie padding/alignment die evtl. querschießen können usw. Nicht zuletzt greifst du mit dem reinterpret_cast auf die privaten Member der complex-Objekte zu, brichst also mutwillig die Kapselung und das ist so oder so möglichst zu vermeiden (manch einer würde sagen verabscheuungswürdig ;))

    Was Arrays und Iteratoren angeht: ein Iterator für ein Array ist ganz einfach ein Pointer.

    Ums mal pragmatisch zu machen, zwei Versionen wie du die Werte umkopieren kannst, ganz ohne große <algorithm>-Spielereien:

    typedef std::vector<std::complex<double> > ComplexVector;
    ComplexVector cplxvec;
    //vector fuellen
    
    //version 1: dynamisches double-array
    double* arr = new double[2*cplxvec.size()];
    double* dptr = arr;
    for (ComplexVector::iterator it = cplxvec.begin(), end = cplxvec.end(); it != end; ++it)
    {
      *dptr++ = it->real();
      *dptr++ = it->imag();
    }
    
    //version 2: std::vector<double>
    std::vector<double> dblvec;
    dblvec.reserve(2*cplxvec.size());
    for (ComplexVector::iterator it = cplxvec.begin(), end = cplxvec.end(); it != end; ++it)
    {
      dblvec.push_back(it->real());
      dblvec.push_back(it->imag());
    }
    

    Am Ende kannst du dann jeweils das Array arr oder den Datenbereich des double-vectors &dblvec[0] an deine entsprechende Funktion übergeben.

    (Falls Fragen oder Verständnisprobleme zum Code da sind, einfach fragen...)

    /edit: Nachtrag: Dass dein Programm ohne Fehler durchläuft ist wie gesagt Glück (auch wenns bei den meisten Implementationen von complex so sein wird). Es ist aber nicht garantiert, dass es immer geht, d.h. es ist nicht portabel.



  • duwan schrieb:

    Läuft bei mir ohne Probleme durch. Ich glaube ich bin auf der Sicheren Seite Danke für eure Hilfe.

    @asc
    Da nach den Aussagen der Anderen scheint mir der Speicher des Vectors innerhalb des c++ Standard zusammenhängend zu sein, und das ist das einzige was ih hier benutzte...

    Nein, du benutzt mehr als nur die Tatsache das der Speicher zusammenhängend vorliegt. Du gehst auch davon aus das im std::complex die doublewerte an exakt den selben Stellen und in der gleichen Reihenfolge liegen.

    Nachtrag:
    Du verletzt eine wirklich sinnvolle Regel: Programmiere immer gegen die Schnittstelle, nie gegen die Implementierung.



  • @pumuckl

    Ich glaube ich habe deinen code verstanden bis auf folgendes:

    {
      *dptr++ = it->real();
      *dptr++ = it->imag();
    }
    

    warum das ++ bei *dptr++ ??

    Ach ja muss jetzt in ein Seminar. bin in spätestens 1.5 Stunden wieder da.



  • Tachyon schrieb:

    Simon2 schrieb:

    [...]

    Ne, das ist schon ein technisches Problem. Es soll ein Vektor aus std::complex<T> ist ein Array der Form [Re Im Re Im ...] umgewandelt werden....

    Das vermutest Du. Aber meine Frage bleibt, ob es das ist, was gewollt ist.
    ... und inzwischen hat duwan das schon beantwortet:

    duwan schrieb:

    ...Also bei meinem Problem ist die Anordnung der Real und Imaginärteile total egal, da die Routine der NAG-Bibliothek sowieso nur mit real-Teilen arbeitet, ...

    Ergo: Doch nicht "egal", weil nur die Realteile interessieren (die man nur rausbekommt, wenn man weiß, wo sie "liegen" (= ihre Anordnung kennt) :p ;).

    Und es bleibt in erster Linie eine fachliche Aufgabe, aus einer komplexen Zahl den Realteil zu extrahieren. Es ist halt nich einfach ein "cast", bei dem eine technische Repräsentation in eine andere deselben Wertes umgewandelt wird, sondern es ist eine echte Abbildung, die den Wert verändert (jedenfall im Allgemeinen).

    Gruß,

    Simon2.



  • duwan schrieb:

    @pumuckl

    Ich glaube ich habe deinen code verstanden bis auf folgendes:

    {
      *dptr++ = it->real();
      *dptr++ = it->imag();
    }
    

    warum das ++ bei *dptr++ ??

    Der operator++ ist der sogenannte Inkrement-Operator. Je nachdem ob er vor oder hinter dem Objekt steht auf das er angewandt wird, wird der Wert vor der Erhöhung (postinkrement, das ++ steht dahinter) oder nach der erhöhung (präinkrement, das ++ steht davor) zurückgegeben.
    dptr++ heißt also "erhöhe den Pointer dptr um eins und gib den alten (Adress-)Wert zurück". Die erste der beiden Zeilen heißt also insgesamt "schreibe den realwert an die Stelle, auf die dptr zeigt und erhöhe diesen".
    Das ist im Grunde nichts anderes als bei Iteratoren (wie oben geschrieben, SIND Pointer die Iteratoren eines Arrays): wenn ich den operator ++ mehrfach auf einen Pointer anwende, gehe ich Schritt für Schritt durch das Array, in das der pointer zeigt (oder durch irgendwelchen kruden Speicher, wenn der Pointer nicht in ein Array zeigt - das wird dann irgendwann vermutlich mit Zugriffsfehlern quittiert...)



  • Alles klar. Wuste schon was ein Increment ist, nur nicht das man es auch auf Pointer anwenden kann

    Also denke ich werde das ganze noch mal überdenken wie ich das jetzt mache. Vielen Dank für eure Hilfe.

    Lg Duwan


Anmelden zum Antworten