2016-07-26 18 views
0

Hallo Ich arbeite an Socket-Übersetzung zwischen zwei Protokollen. Ich lese aus einer Binärdatei und speichere den geparsten Header in einem Array vom Typ uint32_t. Dann nehme ich die Felder aus dem Array und wandle sie in entsprechende Typen um. Bisher funktioniert uint32_t/int32_t/uint16_t bis int32_t funktioniert gut.C - kombiniere zwei uint32_t zu einem Doppel

Allerdings bekomme ich alle Arten von falschen Ausgaben beim Versuch, zwei Uint32_t (nacheinander anhängen) und dann konvertieren diese 64-Bit-lange Daten in ein Doppel.

Als Neuling zu C-Programmierung habe ich Probleme mit der Computer-Methodik von double/float Darstellung.

Im Grunde, was ich will, ist zu tun: ohne das Bitmuster der beiden zu verändern uint32_t, Concast verketten nacheinander eine 64-Bit-Daten zu machen, wandeln dann die Daten als double. Das Wichtigste ist, das Bitmuster nicht zu ändern, da dieser Teil des Bitstroms ein double sein soll.

Das folgende ist ein Teil des Codes:

uint32_t* buffer = (uint32_t*) malloc (arraySize * sizeof(uint32_t)); 

... 

double outputSampleRate = ((union { double i; double outputSampleRate; }) 
    { .i = ((uint64_t)buffer[6] << 32 | (uint64_t)buffer[7])}).outputSampleRate; 

Daten in Eingabedatei:

35.5 

Wert nach meinem Code:

4630192998146113536.000000 

, auch gibt es eine bessere Art und Weise die Socket-Header-Analyse zu behandeln?

+2

"Ich bekomme alle Arten von falschen Ausgaben" -> Was war Ihre Eingabe, Ausgabe und erwartete Ausgabe. – chux

+1

Nicht alle Bitmuster, die mit 2 'uint32_t' möglich sind, können zu einem gültigen/eindeutigen 'double' führen. Es hängt vom 'doppelten' Format und möglichen Nicht-Ein-Zahlen ab. – chux

+1

"gibt es eine bessere Möglichkeit, die Socket-Header-Analyse zu behandeln?" Sende den Code, den du versucht hast, um gutes Feedback zu bekommen, sonst ist das einfach zu weit gefasst. – chux

Antwort

1

Die Neuinterpretation von Bitmustern über eine Union erfordert, dass die Union-Elemente den richtigen Typ aufweisen. Ihre Union hat zwei doubles, also wenn Sie von einem lesen, wird es den gleichen Wert wie der andere haben.Die Umwandlung von uint32_t zu double wird eine sein, die numerische Ergebnisse beibehält und den "Müll" erklärt, der wirklich nur die double ist, die als eine Ganzzahl neuinterpretiert wird. Sie müssen auch die richtige Byte-Reihenfolge verwenden (niedriges Wort zuerst? Hohes Wort zuerst?) Und der einfachste Weg, dies zu tun, ist, Bit-Verschiebung insgesamt zu vermeiden.

double outputSampleRate = ((union { uint32_t i[2]; double d; }) 
    { .i = { buffer[6], buffer[7] } }).d; 

könnten Sie uint64_t i aber ... warum die Mühe?

Sie auch memcpy() nutzen, um die Bytes ...

double outputSampleRate; 
memcpy(&outputSampleRate, &buffer[6], sizeof(outputSampleRate)); 

Die üblichen Warnungen zu kopieren gelten: Während diese Lösungen relativ tragbar sind, nehmen sie nicht Endian Probleme zu berücksichtigen, und sie werden nicht Arbeiten Sie an Systemen, die Ihre Annahmen über zB verletzen wie groß ein double ist, aber es ist im Allgemeinen sicher, diese Annahmen zu treffen.

1

Ihre Vereinigung Definition ist falsch, möchten Sie i als uint64_t definiert werden:

double outputSampleRate = ((union { uint64_t i; double d; }) 
    { .i = ((uint64_t)buffer[6] << 32 | (uint64_t)buffer[7])}).d; 

Sie könnten auch in eine endianness Problem ausgeführt werden. Versuchen Sie Little-Endian:

double outputSampleRate = ((union { unt64_t i; double d; }) 
    { .i = ((uint64_t)buffer[7] << 32) | (uint64_t)buffer[6]}).d; 

die Bits der Darstellung über ein union Reinterpreting wird tatsächlich von der C-Standard unterstützt und ist als Typ punning bekannt. Es funktioniert nicht garantiert, wenn die Bits einen Trap-Wert für den Zieltyp darstellen.

Sie könnten andere Würfe und Tricks versuchen: Ihr Glück testen und einen Zeiger Guss verwenden:

double outputSampleRate = *(uint64_t*)&buffer[6]; 

andere Art und Weise Art punning zu zwingen, ist die memcpy Funktion zu verwenden:

double outputSampleRate; 
uint64_t temp = ((uint64_t)buffer[7] << 32) | (uint64_t)buffer[6]; 
memcpy(&outputSampleRate, &temp, sizeof(outputSampleRate)); 

Oder einfach :

double outputSampleRate; 
memcpy(&outputSampleRate, &buffer[6], sizeof(outputSampleRate)); 

Aber es scheint nicht garantiert zu arbeiten, auch wenn ich h habe einige Beispiele für beide oben im Produktionscode gesehen.

+0

Nicht in der Stimmung, das zu überprüfen, aber es gibt etwas im Standard (oder einen Defektbericht), dass die "Union" Ansatz sehr gut ist garantiert zu arbeiten. Der 'memcpy' Ansatz OTOH verletzt die effektive Art Regel wie ein Cast. – Olaf

+0

Ich fand es: http://StackOverflow.com/Questions/11373203/accessing-inactive-union-member-undefined- Verhalten –

+0

@JerryJeremiah: ausgezeichnete Referenz für die Art punning! – chqrlie

Verwandte Themen