Vector Template Problem mit Normalisierung bei union type _m128
-
Hallo ich wollte mir ein Template für einen vector schreiben, ein wenig gegoogelt uns siehe da die werden einem hinterhergeworfen

Aber man will ja was lernen, also habe ich mir das ( für mich beste ) aus allen rausgesucht und etwas eigenes gemacht.
Läuft auch alles super. Zumindestens für die standard Datentypen.Also int, float usw. Jedoch wollte ich nun __m128 nutzen. Und da fängt es an interessant zu werden bei der Normalisierung. Hier erst mal ein paar Zeilen code:
#ifndef TKVECTOR_H #define TKVECTOR_H #include <memory.h> template<class T, unsigned int SIZE> class TKVector { public: TKVector() { }; TKVector(const TKVector & iVector) { memcpy(mData, &iVector, sizeof(T) * SIZE ); }; TKVector(const T * iData) { memcpy(mData, iData, sizeof(T) * SIZE); }; TKVector & operator = (const TKVector & iVector) { memcpy(mData, iVector.mData, sizeof(T) * SIZE); return *this; }; const T & operator [] (unsigned int index) const {return mData[index];}; T & operator [] (unsigned int index) {return mData[index];}; const T * data() const {return &mData;}; T * data() {return &mData;}; TKVector & operator += (const TKVector & iVector) { for (int i = 0; i < SIZE; ++i) mData[i] += iVector.mData[i]; return *this; }; TKVector & operator -= (const TKVector & iVector) { for (unsigned int i = 0; i < SIZE; ++i) mData[i] -= iVector.mData[i]; return *this; }; TKVector & operator *= (const T & iScalar) { for (unsigned int i = 0; i < SIZE; ++i) mData[i] *= iScalar; return *this; }; TKVector & operator - () { for (unsigned int i = 0; i < SIZE; ++i) mData[i] = -mData[i]; return *this; }; unsigned int size() const {return SIZE;}; private: T mData[SIZE]; }; template<class T, unsigned int SIZE> bool operator == (const TKVector<T, SIZE> & iLeft, const TKVector<T, SIZE> & iRight) { return memcmp(iLeft.data(), iRight.data(), sizeof(T) * SIZE) == 0; }; template<class T, unsigned int SIZE> bool operator != (const TKVector<T, SIZE> & iLeft, const TKVector<T, SIZE> & iRight) { return memcmp(iLeft.data(), iRight.data(), sizeof(T) * SIZE) != 0; }; template<class T, unsigned int SIZE> TKVector<T, SIZE> operator + (const TKVector<T, SIZE> & iLeft, const TKVector<T, SIZE> & iRight) { TKVector<T, SIZE> result; for (unsigned int i = 0; i < SIZE; i++) result[i] = iLeft[i] + iRight[i]; return result; }; template<class T, unsigned int SIZE> TKVector<T, SIZE> operator - (const TKVector<T, SIZE> & iLeft, const TKVector<T, SIZE> & iRight) { TKVector<T, SIZE> result; for (unsigned int i = 0; i < SIZE; i++) result[i] = iLeft[i] - iRight[i]; return result; }; template<class T, unsigned int SIZE> TKVector<T, SIZE> operator * (const TKVector<T, SIZE> & iLeft, const T & iScalar) { TKVector<T, SIZE> result; for (unsigned int i = 0; i < SIZE; ++i) result[i] = iLeft[i] * iScalar; return result; }; template<class T, unsigned int SIZE> T dot( const TKVector<T, SIZE> & a, const TKVector<T, SIZE> & b ) { T result = a[0] * b[0]; for( unsigned int i = 1; i < SIZE; i++ ) result += (a[i] * b [i]); return result; } template<class T, unsigned int SIZE> T length( const TKVector<T, SIZE> & iVector ) { return sqrt( dot(iVector, iVector) ); } template<class T, unsigned int SIZE> void normalize( TKVector<T, SIZE> & ioVector ) { ioVector *= 1 / sqrt( dot(ioVector, ioVector) ); } template<class T> TKVector< T, 2> createVector( T x, T y ) { TKVector<T, 2> vector; vector[0] = x; vector[1] = y; return vector; }; template<class T> TKVector< T, 3> createVector( T x, T y, T z ) { TKVector<T, 3> vector; vector[0] = x; vector[1] = y; vector[2] = z; return vector; }; template<class T> TKVector< T, 4> createVector( T x, T y, T z, T w ) { TKVector<T, 4> vector; vector[0] = x; vector[1] = y; vector[2] = z; vector[3] = w; return vector; }; typedef TKVector< float, 2 > vector2f; typedef TKVector< float, 3 > vector3f; typedef TKVector< float, 4 > vector4f; typedef TKVector< double, 2 > vector2d; typedef TKVector< double, 3 > vector3d; typedef TKVector< double, 4 > vector4d; typedef TKVector< int, 2 > vector2i; typedef TKVector< int, 3 > vector3i; typedef TKVector< int, 4 > vector4i; #endif // TKVECTOR_HWie man in normalize erkennen kann möchte ich gerne alle elemente mit 1/sqrt(...) multiplizieren. Leider gibt es keinen default ctor der mir die 1 in ein __m128 type unwandelt. __m128 ist auch nur ein union.
operatoren für +,-,*,/ und sqrt habe ich für __m128 schon definiert.
typedef __m128 packet4float; packet4float operator + (const packet4float & iLeft, const packet4float & iRight) { return _mm_add_ps(iLeft, iRight); } packet4float operator - (const packet4float & iLeft, const packet4float & iRight) { return _mm_sub_ps(iLeft, iRight); } packet4float operator * (const packet4float & iLeft, const packet4float & iRight) { return _mm_mul_ps(iLeft, iRight); } packet4float operator / (const packet4float & iLeft, const packet4float & iRight) { return _mm_div_ps(iLeft, iRight); } packet4float sqrt( const packet4float & iValue ) { return _mm_sqrt_ps( iValue ); }Hat jemand einen Vorschlag wie ich das Problem mit der 1 löse?
Und auch einen wie ich einen *= operator für _m128 schreib?Vielen Dank schon einmal.
-
1. nimm mal cpp-Tags - dann kann man vll auch mal was lesen ^^
2. gehören memset, memcpy, memcmp etc nicht gerade zu C++ sondern eher zu C
3. solltest du operator +=, -=, /=, *= als Member nehmen, aber +, - (unär und binär), *, / und sämtliche vergleichsoperatoren sollten außerhalb der klasse deklariert werden...
außerdem ist dein *= falsch und /= ist imho nirgendwo im zusammenhang mit vektoren definiert (mal davon abgesehen, dass es ne skalarmultiplikation (nicht zu verwechseln mit dem skalarprodukt) mit 1/x gibt - aber du hast die funktion ja mit nem zweiten vektor verknüpft)
static inline T dot( const TKVector<T, SIZE> & a, const TKVector<T, SIZE> & b ) { ...1. ist das static dort sinnlos (freie funktionen sind genau das, was eine static memberfunktion wäre) - vll ist das static dort so gar falsch und nicht nur sinnlos - wegmachen würde ich es auf jeden fall
2. wird es vom compiler automatisch versucht als inline zu machen, wenn die funktionsdefinition in der gleichen datei wie auch die funktionsdeklaration zu finden ist
3. würde ich von der bezeichnung dot abraten und das skalarprodukt über den operator * ausrechnen lassen (oder aber zumindest den funktionsname ändern)
4.result = result +...für genau so etwas gibt esresult += ...bb
edit:
Mein Normalize würde so aussehen:template <size_t Tct, typename T> bool /*sucessfully*/ Normalize (TVector <Tct, T> &vec) { const T length = GetLength(vec); //die länge des vektors - die wurzel aus dem kreuzprodukt mit sich selbst if(Equals (length, 0)) //fließkommavergleich auf 0, weil /= 0 ja schon ein wenig undefiniert ist ^^ return false; vec /= length; //aufruf des operator /= (const T rhs) - also die Skalarmultiplikation mit 1/x return true; //alles ok, da der nenner != 0 war... }
-
zu 1. cpp-Tags was sind den das?

zu 2. ja ist C, sehe da aber kein Problem drinn.
zu 3. Ja du hast vollkommen recht.
Das *= ist eigentlich die Skalarmultiplikation der Parameter ist bloss absolut falsch bennant. Natürlich kann man /= weglassen, ist wahrscheinlich auch besser für die performance.
Ich habe es extra dot (ist doch skalarprodukt auf english?) genannt. Mit einem zweiten * operator als skalar komme ich immer durcheinander. Ich denke auch das es so eindeutiger ist und man in der Benutzung nicht ausversehen einen Fehler macht indem man einen scalar statt Vector nimmt.
zu 4. Mir ist klar dass das das ( geil 3x das ) gleiche ist. Nur habe ich noch keinen operator += für einen union Typ (__m128) hinbekommen.
Danke dir schon mal werde gleich mal ein wenig abändern und es editieren.
-
Natürlich kann man /= weglassen, ist wahrscheinlich auch besser für die performance.
Würde ich nicht machen - ich würds als Skalarmultiplikation mit dem Reziprogen lassen... aber du hast als rhs ja nen TKVector - und bei ner Skalarmultiplikation sollte das ein Skalar sein - wie der Name schon sagt ^^ also T
Nur habe ich noch keinen operator += für einen union Typ (__m128) hinbekommen
was willst du machen?
ein += (auf einen vector) mit einem float?
was soll das für ne operation darstellen?außerdem: warum redest du ständig von einem union Typ __m128? (__x sollte so und so für den Compiler reserviert sein) - du müsstest zumindest mal sagen, was ein union typ __m128 ist... und dann zeigst du noch mal genau das stück code, was jetzt nicht so will, wie es soll ^^
bb
-
Hey, ich habe die cpp-tags gefunden

Habe oben mal die Änderungen gemacht. Hoffe das haut soweit alles hin.
Ich möchte sowas hier machen:
TKVector<__m128, 3> test; ... normalize(test)Ich nutze die SSE Befehlserweiterungen. Dabei gibt es einen Typ der heißt __m128, der folgendermaßen definiert ist in xmmintrin.h
typedef union __declspec(intrin_type) _CRT_ALIGN(16) __m128 { float m128_f32[4]; unsigned __int64 m128_u64[2]; __int8 m128_i8[16]; __int16 m128_i16[8]; __int32 m128_i32[4]; __int64 m128_i64[2]; unsigned __int8 m128_u8[16]; unsigned __int16 m128_u16[8]; unsigned __int32 m128_u32[4]; } __m128;Mein Problem ist das normalize nicht geht weil ich die 1 nicht in den Typ __m128 umwandeln kann.
Und es geht auch dot nicht weilich noch nicht rausgefunden habe wie ich einen += operator für diesen Typ mache.
-
Wenn du eh nen template hast, kannst du doch auch einfach die "normalen" Typen nutzen?!
template <typename T, size_t laenge> class TKVector { //... template <typename Trhs> operator += (const TKVector <Trhs, size_t laenge> &rhs) { for (size_t i(0); i != laenge; ++i) { data[i] += T(rhs[i]); } } };allerdings solltest du evtl auch eine Konvertierung von TKVector <T, l> zu TKVector <T2, l2> anbieten - und den so entstehenden Vector entsprechend mit 0en auffüllen oder einfach "abschneiden" und den Wertetyp entsprechend ändern
(macht im mathematischen ja auch durchaus Sinn), also in etwa so:template <typename T, size_t laenge> class TKVector { //... template <typename T_rhs, laenge_rhs> TKVector <T, laenge> copy (const TKVector <T_rhs, size_t laenge_rhs) &rhs) { if (laenge_rhs < laenge) { for(size_t i(0); i != laenge_rhs; ++i) { data[i] = T(rhs.data[i]); } for(size_t i(laenge_rhs); i != laenge; ++i) { data[i] = T(); //kannst auch 0 schreiben, aber ich machs immer so - in der Hoffnung, dass es besser optimiert wird ^^ } } else { for(size_t i(0); i != laenge; ++i) { data[i] = T(rhs.data[i]); } } } /*explicit - konnte ich mich noch nicht ganz entscheiden - aber eher nicht ^^*/ template <typename T_rhs, size_t laenge_rhs> TKVector(const TKVector <T_rhs, laenge_rhs> &rhs) { *this = copy (rhs); } operator += (TKVector rhs) { for (size_t i(0); i != laenge; ++i) { data[i] += rhs[i]; } } };TKVector <float, 3> eins; TKVector <int , 5> zwei; TKVector <float, 5> drei(zwei); drei += eins;sollte so weit eigtl alles logisch sein - falls nicht, frag nach ^^
bb
-
Halcom schrieb:
zu 2. ja ist C, sehe da aber kein Problem drinn.
Wenn du dir der Fehleranfälligkeit und Umständlichkeit von C-Mitteln bewusst bist und diese auch in Kauf nimmst, ist das auch kein Problem.

Oft ist es jedoch so, dass Leute gar nicht die entsprechenden Lösungen in C++ kennen. Statt
memcpy()kannst du beispielsweisestd::copy()verwenden, stattmemcmp()operator==und so weiter...Halcom schrieb:
Ich habe es extra dot (ist doch skalarprodukt auf english?) genannt.
"dot" bedeutet nur Punkt. Skalarprodukt (Punktprodukt) wäre "dot product". Vektorprodukt (Kreuzprodukt) hingegen "cross product".
Halcom schrieb:
Mit einem zweiten * operator als skalar komme ich immer durcheinander.
Für die Vervielfachung (= Multiplikation eines Vektors mit einem Skalar) würde ich schon
operator*definieren. Beim Skalarprodukt finde ich eine Funktion angebracht, da dann auch sicher klar ist, welches Produkt gemeint ist (es gibt nicht nur Skalarprodukt).Halcom schrieb:
union Typ (__m128)
Wie unskilled schon sagte, brauchst du eigentlich keine Union.
unions werden in C++ kaum eingesetzt, dafür sind sie einfach zu unsicher und können nur mit PODs arbeiten. Wenn du ein Template hast, instanziierst du das für einen bestimmten Typen (alsofloat,char,int,std::complexoder irgendwas) - da hast du ja den Typen schon, und natürlich auch dessen Typsicherheit.
-
Wie unskilled schon sagte, brauchst du eigentlich keine Union. unions werden in C++ kaum eingesetzt, dafür sind sie einfach zu unsicher und können nur mit PODs arbeiten. Wenn du ein Template hast, instanziierst du das für einen bestimmten Typen (also float, char, int, std::complex oder irgendwas) - da hast du ja den Typen schon, und natürlich auch dessen Typsicherheit.
Ich brauche ihn eben doch. Weil ich den SSE Befehlssatz nutzen möchte. Dafür gibt es bereits vorgefertigte Methoden die das Handling erleichtern.
Schaut ihr z.B. in der Hilfe mal unter _mm_mul_ps:__m128 _mm_mul_ps(__m128 a , __m128 b );
Ist da schon fertig definiert. Würde ich jetzt mein eigenen SSE2 datentyp machen könnte ich diese intrinsics nicht nutzen und müsste die implementierungen in assembler machen. Und da habe ich eigentlich nict wirklich lust drauf.
Also habe ich nur 2 Möglichkeiten. Entweder ich verabschiede mich für den Datentyp von meinem Template und mache einen spezialisierten Vektor oder aber ich mache meinen eigene SSE2 Datentyp und implementiere die operatoren eben in Assembler anstatt die intrinsics zu nutzen.
-
hmm... hab ich kein plan von - aber ich würde mal behaupten wollen, dass du dir dann das template auch gleich sparen kannst...
allerdings würde ich so was nicht machen - nen vektor ist nur so lange hilfreich, wie ich mit dem datentyp meiner wahl rechnen kann (neben den pods auch mit komplexen oder sehr großen zahlen) - und da würde dein vector dann voll durchfallen... außerdem weiß ich nicht, wie viel geschwindigkeit der sse-befehlssatz bringt - falls es sehr viel schneller ist wird es sich vll doch lohnen aber naja...falls du zeit hast, kannste ja mal zeigen, wie viel schneller er mit sse-befehlssatz rechnen kann als mit "normalem" code?
bb
-
Hallo
Ich möchte etwas ähnliches machen, da ich für einige Funktionen den Typ unterscheiden muss. Die Geschichte ist zwar ziemlicher Missbrauch von Templates, aber ich komme einfach nicht auf eine Lösung. Hat einer vielleicht ne Idee?Der Header
template<typename T> struct data<int> {typedef __m128 datentyp;}; //autsch, unzählige errors template<typename T> struct data<float> {typedef __m128i datentyp;}; //ebenso template <class T> class MyClass { union { T daten1[4]; data<T>::datentyp daten2; } ... void foo(const __m128& a, const __m128& b); void foo(const __m128i& a, const __m128i& b); ... void doSomething(const MyClass<T>& class) {foo(daten2, class.daten2);} // Hier muss ich den Typ wissen damit das richtige foo(...) aufgerufen wird };Das es beim kompilieren knallt steht außer Frage... Gibts da irgendeine Möglichkeit dies umzusetzen?
Typsicherheit ist meinerseits gegeben da nur mit int oder float das Template instanziert wird.
-
Templatespezialisierungen gehen etwas anders:
template<typename T> struct data; template<> struct data<int> { typedef __m128 datentyp; }; template<> struct data<float> { typedef __m128i datentyp; };Das richtige
foowird automatisch durch den Kompiler ausgewählt, da dieser die Datentypen kennt.Grüssli
-
Hey danke dir, ist der Hammer das es so geht!
So klappt es einwandfrei
template<typename T> struct data; template<> struct data<int> { typedef __m128 datentyp; }; template<> struct data<float> { typedef __m128i datentyp; }; template <class T> class MyClass { public: typedef typename data<T>::datentyp DatenTyp; union { T daten1[4]; DatenTyp daten2; } ... void foo(const __m128& a, const __m128& b); void foo(const __m128i& a, const __m128i& b); ... void doSomething(const MyClass<T>& class) {foo(daten2, class.daten2);} };Besser wäre es wahrscheinlich, wenn ich die entsprechenden Methoden spezialisiere, oder?
template<class T> void MyClass<T>::foo(const T& a, const _T& b){...} //Allgemein template<> void MyClass<float>::foo(const __m128& a, const __m128& b){...} // Spezialisierung float template<> void MyClass<int>::foo(const __m128i& a, const __m128i& b){...} // Spezialisierung int