Neuer Datentyp: 48-Bit Int
-
groovemaster schrieb:
Honolulu schrieb:
ich brauche einen Datentyp, der 6 Bytes lange Ganzzahlen frisst. 6 Bytes, also 48 Bits und kein Bit mehr. Gibt es den für C++?
Der Standard schreibt keine direkten Grössen von Ganzzahltypen vor. 6 Bytes müssen noch nicht mal 48 Bit sein.
Honolulu schrieb:
Wenn nicht, kann ich einen selbst erstellen? Wie funktioniert das?
Theoretisch funktioniert das schon. Dazu musst du dir anschauen, was deine Plattform bzw dein Compiler an Ganzzahltypen hergibt. Wenn nicht direkt 48 Bit, dann kannst du immer noch mehrere Werte kombinieren, zB 32 und 16 oder 16 und 16 und 16 oder 32 und 8 und 8 oder ... Natürlich sind dann Berechnungen nicht ganz trivial, da du zB auf Überlauf etc. achten musst. Ist aber alles machbar. Das ganze dann schön in eine Klasse verpackt und du kannst den Typ nutzen wie built-ins.
eine einfachere möglichkeit wäre, sofern vorhanden, einen 64bit integer für die implementierung zu nutzen und die überzähligen bits bei jeder berechnung zu verwerfen (bei unsigned trivial, bei signed wäre es implementationsabhängig (z.b. x86), ob man modulo direkt verwenden kann)
könnte man evtl. auch bitfields einsetzen?
-
Hi,
du könntest mit einem 6 Byte Char-Array deine 48Bit simulieren, und Bit für Bit "reinshiften"( mit <<). Das ganze evtl. mit std::vector und als Klasse funktioniert bestimmt super.
-
camper schrieb:
eine einfachere möglichkeit wäre, sofern vorhanden, einen 64bit integer für die implementierung zu nutzen und die überzähligen bits bei jeder berechnung zu verwerfen (bei unsigned trivial, bei signed wäre es implementationsabhängig (z.b. x86), ob man modulo direkt verwenden kann)
Ich glaube nicht, dass dies für Honolulu eine Lösung ist.
Honolulu schrieb:
ich brauche einen Datentyp, der 6 Bytes lange Ganzzahlen frisst. 6 Bytes, also 48 Bits und kein Bit mehr
-
Funktionieren Bitfelder nur für weniger als 8 Bits?
struct myType { blubb : 48; }
Weiß jetzt gar nicht, muss ein Typ auch davor stehen, oder?
MfG SideWinder
-
Ja, ein Typ muss vornran stehen. Aber mit der größe weiß ich nicht genau, ich glaube immer mindestens die ganze Größe des Datentypes, es sei denn, der compiler findet eine möglichkeit, mehrere fields zusammenzuhängen...
-
groovemaster schrieb:
camper schrieb:
eine einfachere möglichkeit wäre, sofern vorhanden, einen 64bit integer für die implementierung zu nutzen und die überzähligen bits bei jeder berechnung zu verwerfen (bei unsigned trivial, bei signed wäre es implementationsabhängig (z.b. x86), ob man modulo direkt verwenden kann)
Ich glaube nicht, dass dies für Honolulu eine Lösung ist.
Honolulu schrieb:
ich brauche einen Datentyp, der 6 Bytes lange Ganzzahlen frisst. 6 Bytes, also 48 Bits und kein Bit mehr
Wobei man mit einem 64 Bit Typ trotzdem was machen könnte, man darf diesen dann nur nicht für die Speicherung des eigentlichen Wertes benutzen. Vorstellen könnte ich mir folgendes
class int48 { private: int8 value_[6]; // getter int64 value() const { //... } // setter void value(int64 source) { //... } public: int48& operator *=(const int48& rhs) { int64 tmp = value(); tmp *= rhs.value(); value(tmp); return *this; } };
Entsprechend kann man auch alle anderen Operatoren implementieren. Zu beachten ist noch das struct Alignment, was sich leider nur compiler-spezifisch einstellen lässt.
Für unsigned sind die getter/setter Funktionen trivial, lediglich für signed sieht es etwas komplizierter aus. Da sollte man sich mit der 2er Komplement Darstellung etwas näher befassen.
-
Erstmal danke für eure Antworten! Bitfielfs habe ich auch schon in Betracht gezogen, nur gibt mir VC++ jedesmal einen Error, wenn das Bitfield grösser als 32 ist.
NeoInferno schrieb:
Hi,
du könntest mit einem 6 Byte Char-Array deine 48Bit simulieren, und Bit für Bit "reinshiften"( mit <<). Das ganze evtl. mit std::vector und als Klasse funktioniert bestimmt super.Könntest du das bitte etwas genauer erklären? Was soll ich wann reinshiften?
Das Licht am Ende des Tunnels sehend, (
)
Honolulu
-
Nimm doch einfach double, das hat 8 Bytes. Wenns dich nicht stört daß die Genauigkeit etwas leidet.
-
NeoInferno schrieb:
du könntest mit einem 6 Byte Char-Array deine 48Bit simulieren, und Bit für Bit "reinshiften"( mit <<). Das ganze evtl. mit std::vector und als Klasse funktioniert bestimmt super
std::vector ist hier allerdings keine gute Lösung. Dynamisches Speicherhandling ist einfach zu viel Overhead für so einen simplen Datentyp. Da passt, wenn schon 'ne Klasse benutzt werden soll, boost::array besser.
Honolulu schrieb:
Erstmal danke für eure Antworten! Bitfielfs habe ich auch schon in Betracht gezogen, nur gibt mir VC++ jedesmal einen Error, wenn das Bitfield grösser als 32 ist.
Das liegt daran, dass das Bitfeld nicht grösser sein kann als der eigentliche Datentyp.
Doppler schrieb:
Nimm doch einfach double, das hat 8 Bytes
Erstmal wird nirgendwo festgelegt, dass double 8 Byte Datenbreite hat. Und selbst, wenn dem so ist und 1 Byte 8 Bits hat, dann sind das wieviel Bits? 48?
Doppler schrieb:
Wenns dich nicht stört daß die Genauigkeit etwas leidet
Das lässt deinen Vorschlag vollkommen sterben. Bei Gleitkommazahlen kann man ja eínen gewissen Genauigkeitsverlust noch in Kauf nehmen. Bei ganzen Zahlen aber mit Sicherheit nicht.
Wobei noch angemerkt sei, dass eine 64 Bit Gleitkommazahl nach IEEE eine 52 Bit Mantisse hat. Demzufolge musst du nicht mal auf Genauigkeit verzichten.
Aber wie gesagt, eine 64 Bit Gleitkommazahl ist Blödsinn. Dan kannst du auch gleich eine 64 Bit Ganzzahl nehmen.
-
groovemaster schrieb:
Doppler schrieb:
Nimm doch einfach double, das hat 8 Bytes
Erstmal wird nirgendwo festgelegt, dass double 8 Byte Datenbreite hat.
Natürlich. Der IEEE Standard schreibt vor, daß eine Fliesskommazahl "Double Precision" aus 64 Bits besteht. Wenn sich ein Compiler nicht dran hält, weil es der bescheuerte C++ Standard eventuell nicht vorsieht, dann ist das eine andere Sache.
groovemaster schrieb:
Und selbst, wenn dem so ist und 1 Byte 8 Bits hat, dann sind das wieviel Bits? 48?
Steigst du nur in ein Auto, wenn schon drei Personen drin sitzen? Es gibt vernünftige Argumente gegen die Nutzung von double, aber davon hast du nicht eins genannt. Typisch groovemaster: Erstmal alles für Blödsinn erklären was sich seinem beschränkten Verstand entzieht.
-
die groovemaster-methode ließe sich noch vereinfachen. die int48-klasse bräuchte nur benutzerdefinierte konversionen von und nach int64 zu enthalten. der compiler würde den rest dann automatisch erledigen
außerdem müsste man noch ein/ausgabe-operatoren für cin/cout schreiben.
-
ich hab da mal was gebastelt:
#include<iostream> using namespace std; typedef unsigned __int64 uint_64; class uint48 { unsigned char value[6]; public: uint48 (uint_64 x); operator uint_64 (); }; uint48::uint48 (uint_64 x) { value[0]=((unsigned char*)&x)[0]; value[1]=((unsigned char*)&x)[1]; value[2]=((unsigned char*)&x)[2]; value[3]=((unsigned char*)&x)[3]; value[4]=((unsigned char*)&x)[4]; value[5]=((unsigned char*)&x)[5]; } uint48::operator uint_64 () { return (uint_64)value[0] +(((uint_64)value[1])<< 8) +(((uint_64)value[2])<<16) +(((uint_64)value[3])<<24) +(((uint_64)value[4])<<32) +(((uint_64)value[5])<<40); } ostream &operator<< (ostream &os, uint_64 x) { if(x<10) os<<(unsigned)x; else os<<x/10<<x%10; return os; } void main () { uint48 x=100000000001,y=200000000002; cout<<"sizeof(uint48)="<<sizeof(uint48)<<endl; cout<<x<<"+"<<y<<"="<<x+y<<endl; }
das funzt auch schon ganz gut mit meinem VC++ 6.0. es fehlen aber noch vernünftige ein/ausgabe-operatoren und auch sonst muß noch dran gearbeitet werden. ich hab auch erst mal nur ein uint48 anstatt eines int48 programmiert, weil ichs mir mit dem weglassen des vorzeichens erst mal einfacher machen wollte. man kann aber natürlich genauso ein int48 implementieren.
-
Verdoppler schrieb:
groovemaster schrieb:
Doppler schrieb:
Nimm doch einfach double, das hat 8 Bytes
Erstmal wird nirgendwo festgelegt, dass double 8 Byte Datenbreite hat.
Natürlich. Der IEEE Standard schreibt vor, daß eine Fliesskommazahl "Double Precision" aus 64 Bits besteht.
Wir sind hier aber nicht im IEEE Forum, sondern bei C++. Und da wird die genaue Grösse von double nunmal nicht festgelegt. double muss nur mindestens soviel Präzision wie float haben. Zudem muss ja nicht auf jeder Plattform eine Gleitkommazahl nach IEEE dargestellt werden.
Verdoppler schrieb:
Steigst du nur in ein Auto, wenn schon drei Personen drin sitzen?
Versteh ich nicht. Kannst du mir mal aufgrund meines "beschränkten Verstandes" erklären, was das mit dem Thema zu tun hat?
Verdoppler schrieb:
Es gibt vernünftige Argumente gegen die Nutzung von double, aber davon hast du nicht eins genannt.
Sry, wenn ich dich in deiner Ehre gekränkt hab. Aber wenn du Unsinn redest, dann solltest du auch mit Kritik leben können. Daran solltest du neben deinen Argumenten noch etwas arbeiten.
Du kannst mir die vernünftigen Argumente aber auch gerne nennen, bin immer bereit, was Neues zu lernen.
Und wenn sich bei dir der Frust über die eigene Unerfahrenheit etwas gelegt hat, dann liess meinen Beitrag nochmal durch. Dann werden dir die vernünftigen Argumenten sicher bewusst.Wir glauben an dich, du schaffst das schon.
@Konfusius
Du solltest noch op= implementieren.
Ansonstenvoid main ()
-
Konfusius schrieb:
uint48::uint48 (uint_64 x) { value[0]=((unsigned char*)&x)[0]; value[1]=((unsigned char*)&x)[1]; value[2]=((unsigned char*)&x)[2]; value[3]=((unsigned char*)&x)[3]; value[4]=((unsigned char*)&x)[4]; value[5]=((unsigned char*)&x)[5]; }
wenn schon unportabel mit tausenden casts dann bitte richtig unportabel und etwas effizienter
uint48::uint48 (uint_64 x) { *(unsigned*)value=x; // kein cast auf der rechten seite nötig ((unsigned short*)value)[2]=((unsigned short*)&x)[2]; }
-
Du solltest noch op= implementieren.
ist nicht nötig. der compiler nimmt dann automatisch den default-copy-konstruktor. und da die klasse keine resourcen alloziert genügt der ja völlig.
-
wie das ohne casts gehen soll mußt du mir mal erklären. und wenn schon casts dann besser explizit als implizit. man könnte höchstens monieren, daß ich nicht c++-mäßige static_cast<>'s verwendet habe. aber dann wärs noch unleserlicher geworden und es hätte dich noch mehr gestört.
-
was daran unportabel ist mußt du mir auch nochmal erklären.
-
die sache mit der effizienz ist schon richtig. hätte besser unsigned short value[3] anstatt unsigned char value[6] nehmen sollen. aber ich hab ja auch geschrieben, daß an dem code noch gearbeitet werden muß :p
-
-
Konfusius schrieb:
- wie das ohne casts gehen soll mußt du mir mal erklären. und wenn schon casts dann besser explizit als implizit. man könnte höchstens monieren, daß ich nicht c++-mäßige static_cast<>'s verwendet habe. aber dann wärs noch unleserlicher geworden und es hätte dich noch mehr gestört.
mit >>
value[0]=x; value[1]=x>>CHAR_BIT; // etc...
- was daran unportabel ist mußt du mir auch nochmal erklären.
byteorder - auf big endian maschinen würdest du die niederwertigen 16 bit des ursprünglichen 64bit integers wegwerfen.
- die sache mit der effizienz ist schon richtig. hätte besser unsigned short value[3] anstatt unsigned char value[6] nehmen sollen. aber ich hab ja auch geschrieben, daß an dem code noch gearbeitet werden muß :p
war auch kein allzu ernst zu nehmender beitrag von mir
-
mal ein paar unwesentliche gedanken dazu:
#include<iostream> using namespace std; typedef unsigned __int64 u64; class u48 { private: u64 read() const {//TODO: wilde casts und union zum konvertieren vorgesehen, aber //sollte erstmal auch so gehen. aufpassen, daß casts nicht durch //bad alignment auf ia64 unendlich langsam werden und lauter so //sachen halt. return (u64(data[0])<<0)|(u64(data[1])<<16)|(u64(data[2])<<32); } void write(u64 x) { data[0]=x>>0; data[1]=x>>16; data[2]=x>>32; } unsigned int data[3]; public: u48 (u64 x) { write(x); } operator u64 () const { return read(); } u48& operator+=(u48 const& b) { write(read()+b.read()); return *this; } friend u48 operator+(u48 a,u48 b) { return u48(a)+=b; } //rest sollte easy sein. }; int main () { u48 x=100000000001ull,y=200000000002ull; cout<<"sizeof(u48)="<<sizeof(u48)<<endl; cout<<x<<"+"<<y<<"="<<x+y<<endl; }
-
Konfusius schrieb:
Du solltest noch op= implementieren.
ist nicht nötig. der compiler nimmt dann automatisch den default-copy-konstruktor.
Was denn nun, default- oder copy-ctor?
Schon klar, was du meinst. Aber mit op= könnte es durchaus effizienter sein, wenn der Compiler es nicht schafft, das temporäre Objekt vernünftig wegzuoptimieren.Konfusius schrieb:
man könnte höchstens monieren, daß ich nicht c++-mäßige static_cast<>'s verwendet habe. aber dann wärs noch unleserlicher geworden
Nicht unbedingt.
value[0]=((unsigned char*)&x)[0]; value[0]=reinterpret_cast<unsigned char*>(&x)[0];
Ich finde zB keine Zeile wirklich leserlicher.
volkard schrieb:
unsigned int data[3];
Immer noch DOS am laufen?
-
groovemaster schrieb:
volkard schrieb:
unsigned int data[3];
Immer noch DOS am laufen?
lol
-
gibt es eigentlich eine garantie, dass sizeof(u48) tatsächlich nur 6byte ist (auf den üblichen plattformen) ? immerhin ist es kein POD. diesbzgl. ist mir sowieso nicht ganz klar, was gefordert ist:
a) der wertebereich des typs entspricht genau dem eines 48bit unsigned integers, also 0..2^^48-1
b) der typ beansprucht genau 48bit speicher und padding in arrays ist verboten.falls nur a) gefordert ist, könnte man, wie erwähnt, ohne weiters bitfelder benutzen, was den vorteil hat, dass der compiler damit direkt umgehen kann:
typedef unsigned long long u64; struct u48 { u64 value:48; };
das hat allerdings dieselben speicheranforderungen wie ein 64bit integer, also ist b) nicht erfüllt. zum rechnen muss man jetzt bezug auf value nehmen, oder man baut eben eine ganze klasse mit schönem interface wie oben drumherum - dann ist es aber kein POD mehr, was evtl. ungünstig ist.