Laden...

Auslesen aus Byte Array und umwandeln zu Double

Erstellt von Sumsum vor 5 Jahren Letzter Beitrag vor 5 Jahren 2.481 Views
S
Sumsum Themenstarter:in
5 Beiträge seit 2018
vor 5 Jahren
Auslesen aus Byte Array und umwandeln zu Double

Hallo,

Ich versuche gerade eine 8 BYTE REAL Gleitkommazahl aus einem BYTE Array was ich vorher aus dem Inhalt einer Datei generiert habe als Gleitkommazahl korrekt ausgeben zu lassen.

Ich bin momentan soweit das ich weiß das ein 8 BYTE Real das gleiche ist wie ein double. Deswegen müsste es doch rein logisch möglich sein die 8 Byte auszulesen und direkt der Variable zuzuordnen.
Funktioniert aber leider nicht so wie ich mir das vorstelle.

Zur Veranschaulichung:

Mein Byte Array Array zu Kontrollausgabe des HEX Wertes der in der Datei gespeichert ist.

	
uint64_t ergebnis = 0;
for (int i = 0; i < 8; i++){
	ergebnis = file_buf[inhalt + i];
    wsprintf(Ausgabebuf + i*2, L"%02X", ergebnis);
}

Ergibt in der Ausgabe den Richtigen Hex Wert aus der Datei
C1D6D420937EE766

wsprintf muss nutzen da ich mit einer API Arbeite nutzen da und auf einen whitecharbuffer angewiesen bin.

Probiert habe ich bisher folgendes


ergdouble =  (double)*&file_buf[inhalt];
swprintf(Ausgabebuf, 200, L" Datentyp: REAL (8-byte): %Lf\n", ergdouble);
OutputMessage(Ausgabebuf, 0);

Ausgabe: Datentyp: REAL (8-byte): 193.000000

	
ergebnis = file_buf[inhalt]<<24;
ergebnis = ergebnis + (file_buf[inhalt + 1] << 16);
ergebnis = ergebnis + (file_buf[inhalt + 2] << 8);
ergebnis = ergebnis + ((file_buf[inhalt + 3]));
ergebnis = ergebnis << 32;
ergebnis = ergebnis + (file_buf[inhalt + 4] << 24);
ergebnis = ergebnis + (file_buf[inhalt + 5] << 16);
ergebnis = ergebnis + (file_buf[inhalt + 6] << 8);
ergebnis = ergebnis + (file_buf[inhalt + 7]);

memcpy(&ergdouble, &ergebnis, 8);
wsprintf(Ausgabebuf, L"Hexwert ergebnis8 Byte %16X", ergdouble);
OutputMessage(Ausgabebuf, 0);
swprintf(Ausgabebuf, 200, L" Datentyp: REAL (8-byte): %Lf\n", ergdouble);
OutputMessage(Ausgabebuf, 0);

Ausgabe:
Hexwert ergebnis8 Byte 937EE766
Datentyp: REAL (8-byte): -1532001869.982873

Laut der Seite: http://www.binaryconvert.com/

müsste aber -1.532002893982 herauskommen bzw. wird das Komma falsch berechnet.

Was mache ich falsch das er das Komma an die falsche stelle bei der Zahl setzt und wie kann ich das beheben?

Verwundern tud mich auch das er bei der Kontrollausgabe des Hexwertes mir nur 4 Byte ausgibt und nicht alle 8.

Ich hoffe jemand kann mir einen Tipp geben, sonst wünsche ich einen sonnigen Dienstag.

Mfg

4.931 Beiträge seit 2008
vor 5 Jahren

Hallo,

du bist hier im falschen Forum, denn hier geht es um C# (.NET) und nicht um C oder C++.

Aber probiere mal


ergdouble =  *(double *)&file_buf[inhalt]; // <-- richtig dereferenzieren
swprintf(Ausgabebuf, 200, L" Datentyp: REAL (8-byte): %f\n", ergdouble); // <-- "%f" statt "%Lf"
OutputMessage(Ausgabebuf, 0);

PS: Bei "%X" wird ein "unsigned int" als Parameter angenommen und ausgegeben (der üblicherweise 4 Byte groß ist), s.a. printf

S
Sumsum Themenstarter:in
5 Beiträge seit 2018
vor 5 Jahren

Hallo und danke für die Antwort.

Das ist schlecht genau das wollt ich vermeiden, verschieben scheint aber im nachinein nciht mehr zu gehen

Da kommt leider auch nichts gescheits heraus


ergdouble = *(double *)&file_buf[inhalt];
swprintf(Ausgabebuf, 200, L" Datentyp: REAL (8-byte): %f\n", ergdouble);
OutputMessage(Ausgabebuf, 0);

AUSGABE:
Datentyp: REAL (8-byte): 51113106308435015981811578495645680980879383926529502009152900803243277129437525141846371360624779921946093065937968128228688033470688126058743367292084728706504858685850370

Ok das schonmal ne Antwort bezüglich dem Hex Wert. Die Paramerter %A bzw. %a kann scheint wsprintf bzw. swprintf nicht zu kennen, er gibt mir jeweils nur den Buchstaben aus. Mich wundert es nur da ich ja extra den Parameter für die 8 Bytes mit übergebe.

656 Beiträge seit 2008
vor 5 Jahren

Und vielleicht der Vollständigkeit halber: so ein Cast ist eigentlich undefined behavior, und sollte vermieden werden. Sogesehen werden die gebräuchlichsten Compiler das richtige machen (sofern nix anderes in der Nähe ist, was eventuell optimierbar aussieht), aber sicherer ist ein std::memcpy der jeweiligen Bytes in den gewünschten Zieltyp:

double ergdouble;
std::memcpy(&ergdouble, &file_buf[inhalt], sizeof(double));
// use ergdouble

Stichwörter type-punning, strict-aliasing und undefined behavior

S
Sumsum Themenstarter:in
5 Beiträge seit 2018
vor 5 Jahren

Danke für den Tipp, das arbeite ich mit ein.

Ich geh aber auch den Zwischenschritt über

ergebnis = ergebnis << 32;

mit ein, weil er mir Undefiniertes Verhalten ansagt wenn ich direkt auf

ergebnis = file_buf[inhalt]<<56;

gehe wobei ich ja eigentlich inerhalb einer 64Bit Variable verschiebe

16.806 Beiträge seit 2008
vor 5 Jahren

Weiß zwar nicht 100%, was Du mit dem "64 Bit Variable" ausdrücken willst, aber in .NET Framework ist der Prozess nur dann garantiert 64 Bit, wenn Du ihn auch mit dem 64-Bit Flag baust.
"Any CPU" ist keine Garantie, dass dies als 64 Bit Prozess auf einer 64 Bit-fähigen CPU läuft.
Die übliche Fall ist, dass eine .NET Anwendung in einem 32-Bit Prozess läuft.

Wenn es sich um eine Anwendung handelt, deren Build man nicht ändern kann (zB. Drittanwendung), gibt es im Windows 10 und .NET SDK ein CorFlag Utility, mit dem man ebenfalls die x64 Ausführung via Any CPU erzwingen kann.

S
248 Beiträge seit 2008
vor 5 Jahren

Hallo Sumsum,

ich vermute, dass file_buf ein byte-Pointer ist (char*/unsigned char*/BYTE*).
Was willst du mit dem shiften erreichen? Vermutlich shiftest du einen byte Wert!?

Wenn du einen double auslesen willst, sollte der Code von Th69 funktionieren (dieser castet deinen byte-Pointer in einen double-Pointer).
Prüfe doch bitte erstmal was für einen Pointer-Typ du hast. Dann stell sicher, dass du an die richtige Stelle zugreifst (Index-Operation) um den double zu lesen. Dann könnte es noch sein, dass der Wert als BigEndian gespeichert wurde?!

Grüße
spooky

S
Sumsum Themenstarter:in
5 Beiträge seit 2018
vor 5 Jahren

Hallo 🙂

@Abt
Mit den 64 Bit wollte ich nur sagen das es eine 8 Byte Variable ist in der ich theoretisch auch 56 Bit nach links shiften kann. Sry für diese Ausrucksweise.

@ Spook

Das stimmt ich habe einen Byte Pointer Definiert

BYTE *file_buf = (BYTE*)HeapAlloc(heap, 0, file_size);

Das shiften war einer von vielen Versuchen der bei einem 4 Byte REAL ( Float ) funktioniert hat.
Ich hab das dann für den 8 Byte REAL übernommen und gehofft das es genauso funktioniert. Der erste Gedanke dabei war das er mir die 8 Byte genauso wie sie in dem Byte Array stehen auch in die double bzw. uint64_t variable schreibt.

Das ich an der richtigen Stelle zugreife kann ich ziemlich sicher sagen da die Kontrollausgabe ja korrekt angezeigt wird.
Ebenfalls richtig liegst du mit dem BigEndian, ich habe Th69 Lösung auch schon in Kombination mit _byteswap_uint64 versucht, funktioniert aber leider auch nicht wie gewünscht.

Ich hatte das vorher nicht erwähnt weil es in der 4 Byte Lösung ohne byteswap funktioniert.
Wenn ich die Byte von Hand umrechne und in die jeweiligen online konverter eingebe bekomme ich auch keine sinnvollen Werte heraus.

Zur Vervollständigung mal die funktionierende 4 Byte Lösung.


ergebnis = file_buf[inhalt]<<24;
ergebnis = ergebnis +(file_buf[inhalt + 1] << 16);
ergebnis = ergebnis + (file_buf[inhalt + 2] << 8);
ergebnis = ergebnis + (file_buf[inhalt + 3]);

memcpy(&ergfloat, &ergebnis, 4);

swprintf(Ausgabebuf, 200, L"Datentyp: REAL:\t%f\n", ergfloat);

W
955 Beiträge seit 2010
vor 5 Jahren

... weil er mir Undefiniertes Verhalten ansagt ...

4.931 Beiträge seit 2008
vor 5 Jahren

@Sumsum: Was ich dir sagen wollte, war, daß du besser in einem C oder C++ Forum dafür nachfragen solltest (nicht in diesem C#-Forum)...

Bis auf Little- bzw. Big-Endian müßte mein gezeigter Code in Ordnung sein.

PS:
@Abt: memcpy und swprintf sind eindeutig C - k.A. wie du mit diesem Thread umgehen möchtest (nach Offtopic verschieben?).

S
Sumsum Themenstarter:in
5 Beiträge seit 2018
vor 5 Jahren

Alles klar, nachdem ich mich mal über die Unterschiede zwischen C und C# belesen habe wird mir erst klar, wie falsch ich hier bin.

Ich bedanke mich trotzdem für die Mühe, die Zeit und wünsche einen angenehmen Sommer 🙂