Forum: PC-Programmierung double Binärisieren und zurück


von Peter (Gast)


Lesenswert?

Hallo,
ich möchte verschiedene Daten über eine Netzwerkverbindung schicken. Um 
diese auf die Netzwerkverbindung zu schreiben verwende ich die write 
Funktion.
Nun möchte ich alle Werte die ich habe vorher Binärisieren, damit ich 
nicht einen langen String schicken muss. Wie mache ich sowas am 
effizientesten? Vor allem auch wieder zurück. Ich weiß zwar an welcher 
stelle das double im Datenstrom ist aber wie kann ich es dort wieder 
möglichst einfach herausholen?

char buffer[100]
double a;
int_32t b;
float c;
Im Buffer soll dann aaaaaaaabbbbcccc stehen. Am anderen Rechner will ich 
dann wieder abc bekommen. Wenn es nur ein Wert wäre würde ich
buffer[0] = (char) a;
machen. Aber da bin ich mir nicht sicher ob er dann nur ein Byte 
kopiert.

By the way: Sind double und float auf jeden System 64 bzw. 32bit breit?

Gruß
Peter

von Dennis S. (eltio)


Lesenswert?

Peter schrieb:
> Nun möchte ich alle Werte die ich habe vorher Binärisieren, damit ich
> nicht einen langen String schicken muss. Wie mache ich sowas am
> effizientesten? Vor allem auch wieder zurück. Ich weiß zwar an welcher
> stelle das double im Datenstrom ist aber wie kann ich es dort wieder
> möglichst einfach herausholen?
Was ist "binärisieren"? Dein Beispiel sieht ein bisschen nach RLE aus!?

> Wenn es nur ein Wert wäre würde ich buffer[0] = (char) a;
> machen. Aber da bin ich mir nicht sicher ob er dann nur ein Byte
> kopiert.
Du castest da einen double-Wert auf einen Char-Wert. Das geht nur mit 
Informationsverlust.

> By the way: Sind double und float auf jeden System 64 bzw. 32bit breit?
Nein.

: Bearbeitet durch User
von Jay (Gast)


Lesenswert?

Auf wie viel Portabilität usw. willst du sch...?

Wenn Portabilität, Floating-Point Format, Byte-Order usw. eh ziemlich 
egal sind:
1
double a;
2
uint8_t buffer[100];
3
uint8_t *pos = buffer;
4
uint8_t *dp = (uint8_t *)&a;
5
for(i = 0; i <= sizeof(double); i++) {
6
    *pos++ = *dp++;
7
}
je nach Anwendung kann dir so ein Vorgehen allerdings unendlich Probleme 
bereiten.

von Der Andere (Gast)


Lesenswert?

Du wandlest sie falls notwendig in ein IEEE 754 Format und überträgst 
sie Byte für Byte.
Dabei musst du noch eine Byte Order definieren.
Schau dir an wie es sonst gemacht wird:
Suche nach "net byte order" und "float network byte order".

von Jay (Gast)


Lesenswert?

Jay schrieb:
> for(i = 0; i <= sizeof(double); i++) {
1
for(i = 0; i < sizeof(double); i++) {

von Peter (Gast)


Lesenswert?

Hallo,
binärisieren ist vieleicht etwas unglücklich gewählt. Ich möchte mir ein 
Datentelegramm aufbauen, dass ich in einen Rutsch verschicken kann. Ich 
habe also einen Pointer auf den Anfang und wenn ich diesen Byteweise 
durchgehe schicke ich alle Daten.
>aaaaaaaabbbbcccc
Hier steht jeder Buchstabe für ein Byte. Da ein double meines Wissens 
64bit hat also 8*a. Ein 32bit int braucht 4 Byte.
Gruß
Peter

von Klaus (Gast)


Lesenswert?

Nun, der Gedanke, der augenscheinlich, für Dich im Moment am wichtigsten 
zu begreifen ist, ist der, dass ausnahmslos alle Daten binär, d.h. als 
Folge von 0 und 1, im Rechner gespeichert sind.

Daraus ergibt sich, dass eine "Binärisierung", wenn ich recht verstehe, 
was Du damit meinen könntest, überflüssig ist.

Was aber nun zwei beliebige Folgen von 0 und 1 im Rechner für praktische 
Zwecke nutzbar macht, ist deren Interpretation - als vorzeichenlose 
oder vorzeichenbehaftete Integerzahl, als Fliesskommazahl oder als Folge 
von ASCII-Zeichen oder weiteres.

Dazu bieten Hochsprachen gewisse Sprachmittel (und Maschinensprachen mit 
geringerer Mannigfaltigkeit). Sie beschreiben, wie eine gewisse Bitfolge 
zu interpretieren ist - neben der Tatsache, dass sie erlaubt bestimmten 
Positionen im Speicher einen Namen - den einer Variablen - zuzuordnen.

Ich kenne nun die von Dir erwähnte "write"-Funktion nicht. Aber nehmen 
wir einmal an, - weil dies Deinen weiteren Ausführungen entspricht -, 
das ihr Argument ein
1
char *

sei.

Die allfälligen C Ausdrücke, die man "cast" nennt, eignen sich nicht 
dafür, die Bitfolge, die Du vorderhand als Fliesskommazahl betrachtest - 
und durch
1
double a;

dem Rechner als solche zu interpretieren befohlen hast -, in einen 
anderen Speicherbereich zu kopieren, den Du als
1
char buffer[100]

(übrigens syntaktisch inkorrekt) deklariert hast. Denn dabei wird immer 
zuerst das Argument des cast-Ausdrucks umintpretiert - eben als char. 
Das aber willst Du nicht. Allein, weil ein char üblicherweise viel 
weniger Bits hat als eine Fliesskommazahl.

Was Du eigentlich willst, ist, wie erwähnt, ein kopieren.

Das, dem geneigten Leser eines C-Buches, augenfällige Mittel, sollte die 
Funktion "memcopy" sein. Jedoch wirst Du zutreffend einwenden, dass 
diese ja ein
1
char *

als Argument erwartet; eine Erwartung die Du mit einem double oder float 
nicht ohne weiteres erfüllen kannst.

Dazu gibt es die Möglichkeit, einen Zeiger auf die Fliesskommazahl zu 
verwenden und ihn anstelle der Zahl selbst, umzuwandeln; in einen 
Zeiger auf char.
1
double a;
2
char buffer[100]
3
4
memcpy (buffer, (char *) a, sizeof(double)

Du siehst wiederum einen cast-Ausdruck, was Dich etwas verwundern wird - 
es aber nicht täte, falls Du ein C-Buch gelesen hättest -, denn ich habe 
ja oben behauptet, dass es sich um eine Uminterpretation handelt. Der 
Unterschied ist aber, dass hierbei ein Zeiger uminterpretiert wird, 
eine Adresse, die durch die Uminterpretation aber immer noch auf die 
selbe Speicherstelle zeigt.

Würde man nun, mit dem, auf dass dieser Zeiger zeigt rechnen wollen, in 
dem man seinen Wert mit dem '*'-Operator ermittelt, so ergäbe sich ein 
char - wieder das nicht gewollte. Aber Du willst lediglich kopieren - 
und das erfordert keine Uminterpretation des Inhaltes; der Bitfolge auf 
die der Zeiger zeigt.

Ich hoffe Dir damit eine Idee in den Kopf gepflanzt zu haben, die Dich 
weiter bringt.

von Dirk B. (dirkb2)


Lesenswert?

Klaus schrieb:
> Das, dem geneigten Leser eines C-Buches, augenfällige Mittel, sollte die
> Funktion "memcopy" sein. Jedoch wirst Du zutreffend einwenden, dass
> diese ja ein
> char *
>
> als Argument erwartet; eine Erwartung die Du mit einem double oder float
> nicht ohne weiteres erfüllen kannst.

Das mag beim K&R C richtig gewesen sein.

memcpy erwartet mittlerweile lediglich void*
( http://www.cplusplus.com/reference/cstring/memcpy/ )

In C kann jeder Datenzeiger ohne cast in einen void-Zeiger gewandelt 
werden. Und umgekehrt gilt es ebenso.

von Dennis S. (eltio)


Lesenswert?

Peter schrieb:
> Hier steht jeder Buchstabe für ein Byte. Da ein double meines Wissens
> 64bit hat also 8*a. Ein 32bit int braucht 4 Byte.

NEIN! Die Länge der Datentypen ist bis auf char nicht definiert... das 
hast du doch schon in der ersten Antwort auf dein Posting lesen können. 
:-/

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Klaus schrieb:
> double a;
> char buffer[100]
>
> memcpy (buffer, (char *) a, sizeof(double)
>
> Du siehst wiederum einen cast-Ausdruck, was Dich etwas verwundern wird -
> es aber nicht täte, falls Du ein C-Buch gelesen hättest
Du verdrehst da 2 tatsachen: wer ein C-Bucht gelesen hat wundert sich 
bei dem Code, und das C Buch braucht nich (nur) er.
Diese Ansamlung von Fehlern! Zumindest etwas besser wäre:
1
double a;
2
char buffer[sizeof(a)]; // Semikolon am schluss nicht vergessen
3
4
memcpy (buffer, &a, sizeof(a)); // Schluessende Klammern, Semikolon und Referenzieren von a nicht vergessen

Wobei das aber das ursprungliche Problem nicht löst.

von Mikro 7. (mikro77)


Lesenswert?

Hallo Peter,

du befindest dich im Bereich der Datenserialisierung: 
https://en.wikipedia.org/wiki/Serialization

Es gibt viele Möglichkeiten deine Problemstellung zu lösen. Eine sehr 
einfache Möglichkeit, die oben bereits genannt wurde, ist per memcpy(). 
Also bspw.
1
#include <stdio.h>
2
#include <stdint.h>
3
#include <string.h>
4
5
void serialize(double a,int32_t b,float c,char (*buffer)[16])
6
{
7
  memcpy(buffer+ 0,&a,8) ;
8
  memcpy(buffer+ 8,&b,4) ;
9
  memcpy(buffer+12,&c,4) ;
10
}
11
12
void deserialize(double *a,int32_t *b,float *c,char (*buffer)[16])
13
{
14
  memcpy(a,buffer+ 0,8) ;
15
  memcpy(b,buffer+ 8,4) ;
16
  memcpy(c,buffer+12,4) ;
17
}
18
19
int main()
20
{
21
  double a=42 ; int32_t b=42 ; float c=42 ;
22
  char buffer[16] ;
23
  serialize(a,b,c,&buffer) ;
24
  a=0 ; b=0 ; c=0 ;
25
  deserialize(&a,&b,&c,&buffer) ;
26
  printf("%f %i %f\n",a,b,c) ;
27
  return 0 ;
28
}

Das funktioniert gut solange beide Seiten die gleiche 
Hardwarearchitektur und Compiler nutzen. Möchte man davon unabhängig 
sein, gibt es eine Vielzahl vom weiteren Möglichkeiten. Ich bspw. 
benutze gern die Basic Encoding Rules 
(https://en.wikipedia.org/wiki/Basic_encoding_rules).

Edit: Spelling

: Bearbeitet durch User
von Kaj (Gast)


Lesenswert?

Boost

Kapitel 64. Boost.Serialization
http://dieboostcppbibliotheken.de/boost.serialization

von Klaus (Gast)


Lesenswert?

Daniel A. schrieb:
> Klaus schrieb:
>> double a;
>> char buffer[100]
>>
>> memcpy (buffer, (char *) a, sizeof(double)
>>
>> Du siehst wiederum einen cast-Ausdruck, was Dich etwas verwundern wird -
>> es aber nicht täte, falls Du ein C-Buch gelesen hättest
> Du verdrehst da 2 tatsachen: wer ein C-Bucht gelesen hat wundert sich
> bei dem Code, und das C Buch braucht nich (nur) er.
> Diese Ansamlung von Fehlern! Zumindest etwas besser wäre:

Das war Absicht. Aber das kann ich natürlich nicht beweisen.
Es geht darum, das der TO tatsächlich liest. Das Gegenteil hätte sich 
bei einer Nachfrage erwiesen.

>
>
1
> double a;
2
> char buffer[sizeof(a)]; // Semikolon am schluss nicht vergessen
3
> 
4
> memcpy (buffer, &a, sizeof(a)); // Schluessende Klammern, Semikolon und 
5
> Referenzieren von a nicht vergessen
6
>
>
> Wobei das aber das ursprungliche Problem nicht löst.

Nun wundere ich mich aber - wenn auch nur gelinde. Das löst das Problem 
durchaus.

von armer ingenieur (Gast)


Lesenswert?

Der wohl einfachste Weg:
1
typedef struct paket_s {
2
    int32_t i;
3
    float   f;
4
    double  d;
5
} paket_t;
6
7
void send (int32_t i, float f, double d){
8
    paket_t p;
9
    p.i = i;
10
    p.f = f;
11
    p.d = d;
12
13
    write(&p);
14
}

Ist halt nicht zu 100% portabel, weil paket_t auf unterschiedlichen 
Systemen möglicherweise unterschiedlich dargestellt wird.

von Peter D. (peda)


Lesenswert?

Beispielfunktionen für float <-> 32Bit:
1
static inline uint32_t mkp_u32( float val )    // make pointer from float to uint32_t
2
{
3
  union{
4
    uint32_t u32;
5
    float f;
6
  } out;
7
  out.f = val;
8
  return out.u32;
9
}
10
11
static inline float mkp_fl( uint32_t val )      // make pointer from uint32_t to float
12
{
13
  union{
14
    uint32_t u32;
15
    float f;
16
  } out;
17
  out.u32 = val;
18
  return out.f;
19
}

von tictactoe (Gast)


Lesenswert?

armer ingenieur schrieb:
> typedef struct paket_s {
>     int32_t i;
>     float   f;
>     double  d;
> } paket_t;

Achtung! Als Datenpaket ist eine struct eher ungeeignet, weil da noch 
Padding-Bytes dazwischen sein können. Die Lösung von S. J. ist viel 
brauchbarer.

Peter D. schrieb:
> static inline float mkp_fl( uint32_t val )      // make pointer from
> uint32_t to float
> {
>   union{
>     uint32_t u32;
>     float f;
>   } out;
>   out.u32 = val;
>   return out.f;
> }

Bitte verbreitet diese Lösung nicht mehr! Von einer Union darf nur der 
Member gelesen werden, der zuletzt beschrieben worden ist. Alles andere 
ist Undefined Behavior, also vielleicht geht's, aber drauf verlassen 
kann man sich nicht.

von Peter D. (peda)


Lesenswert?

tictactoe schrieb:
> Bitte verbreitet diese Lösung nicht mehr! Von einer Union darf nur der
> Member gelesen werden, der zuletzt beschrieben worden ist.

Dann bliebe ja die einzig erlaubte Nutzung einer Union die 
Doppelbelegung von RAM, was heutzutage niemand mehr braucht.

Solche Umwandlungen sind eh immer Target und Compiler abhängig, d.h. man 
muß prüfen, ob sie richtig funktionieren.
Man kann sie ja in einen Guard kapseln, der bei nicht geprüftem 
Compiler/Target ein #error ausgibt.

Ein universelle Lösung gibt es in C nicht.

von Georg (Gast)


Lesenswert?

Peter schrieb:
> Nun möchte ich alle Werte die ich habe vorher Binärisieren

Die Frage ist, ob das überhaupt so eine gute Idee ist. Natürlich ist 
eine Text-Darstellung von Zahlen länger, aber das ist ja nur von 
Interesse, wenn die Übertragungskapazität knapp ist, also eher selten. 
Dafür kann man eine ASCII-Meldung mit jedem Terminalprogramm mitlesen, 
bei Binärkodierung ist man dagegen beim Debuggen und Testen ziemlich 
aufgeschmissen. Es sei denn, man gehört zu den wenigen Menschen, die 
Hex-Code direkt lesen können und im Kopf aus 16 Hex-Zeichen die 
Double-Zahl bilden.

Die weitaus meisten Internet-Protokolle arbeiten mit Text, die Erfinder 
haben sich da schon was dabei gedacht.

Georg

von mikro77 (Gast)


Lesenswert?

> Dann bliebe ja die einzig erlaubte Nutzung einer Union die
> Doppelbelegung von RAM, was heutzutage niemand mehr braucht.

Och. "Niemand" ist ein bisschen hart. Da soll es noch ein paar 
Anwendungen geben die auf Laufzeit und Speicher achten (müssen)...

> Solche Umwandlungen sind eh immer Target und Compiler abhängig, d.h. man
> muß prüfen, ob sie richtig funktionieren.
> Man kann sie ja in einen Guard kapseln, der bei nicht geprüftem
> Compiler/Target ein #error ausgibt.

Das reicht in vielen Fällen aus. Insbesondere hier wo einige Anfänger 
unterwegs sind muss man es nicht "unnötig" kompliziert machen.

> Ein universelle Lösung gibt es in C nicht.

Würde ich jetzt nicht so sagen.

https://en.wikipedia.org/wiki/Comparison_of_data_serialization_formats

Muss man halt gucken ob der Aufwand lohnt.

von Peter D. (peda)


Lesenswert?

Georg schrieb:
> Die weitaus meisten Internet-Protokolle arbeiten mit Text, die Erfinder
> haben sich da schon was dabei gedacht.

Ich bin auch grad dabei, ein CAN-Protokoll von Binär auf Text 
umzustellen, die Kollegen wollen das so.

Allerdings muß man bei float als Text auf Rundungsfehler achten, d.h. 
Sender und Empfänger können zu unterschiedlichen Ergebnissen kommen.

Z.B. gab es ein Gerät, was eine Meßreihe aufnehmen sollte. Der PC kam 
z.B. auf 10 Meßwerte, der Sensor meinte aber nur 9 schicken zu müssen 
und schon gab es ein Timeout. Die Lösung war dann, daß der Sensor 
erstmal die Anzahl schickte.

von Georg (Gast)


Lesenswert?

Peter D. schrieb:
> Allerdings muß man bei float als Text auf Rundungsfehler achten, d.h.
> Sender und Empfänger können zu unterschiedlichen Ergebnissen kommen.

Ja, wenn unterschiedliche Prozessoren  Sprachen  Libraries verwendet 
werden. Es ist aber gerade die Aufgabe eines Protokolls, von so etwas 
unabhängig zu sein, ein Grund mehr, eben keine Binärdaten zu übertragen.

Georg

von Peter (Gast)


Lesenswert?

Hallo nochmal,
danke für eure Tips. Ich habe es mal einen Ansatz mit memcpy versucht. 
Das ging auch soweit. Trotzdem werde ich nochmal überlegen ob ich es nur 
via Strings mache oder eine komplett andere Lösung anstrebe.
Gruß
Peter

von Rolf M. (rmagnus)


Lesenswert?

Georg schrieb:
> Peter D. schrieb:
>> Allerdings muß man bei float als Text auf Rundungsfehler achten, d.h.
>> Sender und Empfänger können zu unterschiedlichen Ergebnissen kommen.
>
> Ja, wenn unterschiedliche Prozessoren  Sprachen  Libraries verwendet
> werden.

Nein, nicht nur dann.

> Es ist aber gerade die Aufgabe eines Protokolls, von so etwas
> unabhängig zu sein, ein Grund mehr, eben keine Binärdaten zu übertragen.

Du hast aber gemerkt, daß es gerade um die Probleme geht, die daraus 
entstehen, wenn man die Daten nach Text wandelt?

von Matthias L. (Gast)


Lesenswert?

Peter D. schrieb:
> Die Lösung war dann, daß der Sensor
> erstmal die Anzahl schickte.

Und schon waren es wieder zehn ;-)

von tictactoe (Gast)


Lesenswert?

Peter D. schrieb:
> tictactoe schrieb:
>> Bitte verbreitet diese Lösung nicht mehr! Von einer Union darf nur der
>> Member gelesen werden, der zuletzt beschrieben worden ist.
>
> Dann bliebe ja die einzig erlaubte Nutzung einer Union die
> Doppelbelegung von RAM, was heutzutage niemand mehr braucht.

Ja, richtig.

Peter D. schrieb:
> Solche Umwandlungen sind eh immer Target und Compiler abhängig, d.h. man
> muß prüfen, ob sie richtig funktionieren.
> Man kann sie ja in einen Guard kapseln, der bei nicht geprüftem
> Compiler/Target ein #error ausgibt.
>
> Ein universelle Lösung gibt es in C nicht.

Da bin ich definitiv anderer Meinung. Ob die Lösung nun lautet 
"Dezimalzahl in ASCII-Ziffern mit der führenden Ziffer zu erst, 
abgeschlossen durch ein Leerzeichen" oder "Ziffernwerte zur Basis 256, 
niederwertigste zuerst, genau 4 davon", ist ja nur Definitionssache. 
Gemeinsam haben beide, dass das Format nicht durch Code festgelegt ist, 
sondern "von außen", und dass sich für beide ein wohldefiniertes 
C-Programm schreiben lässt.

von armer ingenieur (Gast)


Lesenswert?

tictactoe schrieb:
> Achtung! Als Datenpaket ist eine struct eher ungeeignet, weil da noch
> Padding-Bytes dazwischen sein können.

Schrieb ich ja. Man sollte dabei halt wissen was man macht. Aber das 
gleiche Programm auf zwei gleichen Controllern sollte da grundsätzlich 
nie ein Problem sein. Und im Endeffekt haben wir hier ein Industriegerät 
laufen mit selbstgestricktem Ethernetstack inkl. TCP/IP und diversen 
Bussystemen, in vielen Fällen aufbauend auf das Schema. Natürlich nicht 
portabel zu irgendwelchen Exoten, aber es reicht für die üblichen 
Verdächtigen, man sollte halt aufs Alignment achten um unnötiges Padding 
zu vermeiden.

Georg schrieb:
> Es ist aber gerade die Aufgabe eines Protokolls, von so etwas
> unabhängig zu sein

Wenn es sich um eine reine Kommunikation untereinander handelt, oft zu 
vernachlässigen...

Georg schrieb:
> ein Grund mehr, eben keine Binärdaten zu übertragen.

Datendurchsatz ist einer dafür.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter D. schrieb:
> Allerdings muß man bei float als Text auf Rundungsfehler achten, d.h.
> Sender und Empfänger können zu unterschiedlichen Ergebnissen kommen.

Bei float muss muss immer auf Rundungsfehler und andere Artefakte 
geachtet werden.  Tut man das nicht, kann es auch ohne Datenübertragung 
und mit den gleichen Tools, Optionen etc. unagenehm werden.

Z.B. gibt es FPUs, die intern mit 80 Bits rechnen, wo aber double mit 
weniger Bits dargestellt ist.  Wenn dann ein Zwischenergebnis in einem 
64-Bit Register gespeichert wird, kann es andere Ergebnisse geben bei 
gleicher Arithmetik.

von Rolf M. (rmagnus)


Lesenswert?

Johann L. schrieb:
> Peter D. schrieb:
>> Allerdings muß man bei float als Text auf Rundungsfehler achten, d.h.
>> Sender und Empfänger können zu unterschiedlichen Ergebnissen kommen.
>
> Bei float muss muss immer auf Rundungsfehler und andere Artefakte
> geachtet werden.

Beim Rechnen ja, aber nicht bei der einfachen Übertragung, die ja an 
sich verlustlos möglich sein sollte. Wenn man aber dafür von der Basis 2 
auf die Basis 10 und zurück rechnen muss, entstehen durch die 
Übertragung Verluste. Wenn dann noch die Anzahl der Dezimalstellen nicht 
gegenüber dem Default stark erweitert wird, gibt es noch größere 
Verluste.

> Z.B. gibt es FPUs, die intern mit 80 Bits rechnen, wo aber double mit
> weniger Bits dargestellt ist.  Wenn dann ein Zwischenergebnis in einem
> 64-Bit Register gespeichert wird, kann es andere Ergebnisse geben bei
> gleicher Arithmetik.

Wobei man sagen muß, daß diese "anderen" Ergebnisse eigentlich die 
einzig ISO-C-konforme Umsetzung sind. Jeder Ausdruck - und damit auch 
jedes Zwischenergebnis hat einen definierten Typ und muß mit der 
Genauigkeit dieses Typs gerechnet werden. Das mag in anderen Sprachen 
anders sein, aber hier geht es ja offenbar um C-Code.

von Daniel A. (daniel-a)


Lesenswert?

Klaus schrieb:
Daniel A. schrieb:
1
memcpy (buffer, &a, sizeof(a));
>> Wobei das aber das ursprungliche Problem nicht löst.
> Nun wundere ich mich aber - wenn auch nur gelinde. Das löst das Problem
> durchaus.

Es geht um Daten, die über das Netzwerk versendet werden. Über das 
Zielsystem konnte ich noch nichts finden. Sollte der TO 2 PCs haben, und 
einer verwendet z.B. Big Endian wärend der andere Little Endian 
verwendet, würde es schon nichtmehr funktionieren. Ein Netzwerkprotokoll 
muss klar definiert sein, sonst ist es murks. Ich würde die Daten in 
IEEE754 binary64 Big Endian codieren. Da ich im Internet keine 
brauchbare Implementierung ohne Binaryhacks und mit brauchbaren 
Lizenzbedingungen finden konnte, habe ich selbst einen geschrieben:
https://github.com/Daniel-Abrecht/IEEE754_binary_encoder

PS: Ich kann nicht garantieren, dass ich keinen Fehler gemacht habe, und 
sorry, dass ich erst so spät Antworte: Es war nicht besonders einfach 
diesen Code zu schreiben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Daniel A. schrieb:
> Es geht um Daten, die über das Netzwerk versendet werden. Über das
> Zielsystem konnte ich noch nichts finden.

Zunächst get es darum, die Signatur zu bestimmen, und sich zu überlegen 
wie man mit INF und (signaling(!)) NaN umgeht.  Dazu gibt es 
plattformabhängige Mittel, bei gcc etwa:
1
echo | gcc -xc - -E -dM | grep DBL | grep -v LDBL | sort
1
#define __DBL_DECIMAL_DIG__ 17
2
#define __DBL_DENORM_MIN__ ((double)4.94065645841246544177e-324L)
3
#define __DBL_DIG__ 15
4
#define __DBL_EPSILON__ ((double)2.22044604925031308085e-16L)
5
#define __DBL_HAS_DENORM__ 1
6
#define __DBL_HAS_INFINITY__ 1
7
#define __DBL_HAS_QUIET_NAN__ 1
8
#define __DBL_MANT_DIG__ 53
9
#define __DBL_MAX_10_EXP__ 308
10
#define __DBL_MAX_EXP__ 1024
11
#define __DBL_MAX__ ((double)1.79769313486231570815e+308L)
12
#define __DBL_MIN_10_EXP__ (-307)
13
#define __DBL_MIN_EXP__ (-1021)
14
#define __DBL_MIN__ ((double)2.22507385850720138309e-308L)

Damit kann man schon mal sicherstellen, dass der verwendete 
(De)Serialisierer halbwegs auf die Binärgarstellungpasst, z.B. MIN_EXP, 
MAX_EXP, MANT_DIG, HAS_QUIET_NAN etc.

Hier der Code aus einem Simulator der einen avr-gcc float als 32-Bit 
unsigned aus dem AVR-Speicher bekommt und ins Host-format (double) 
"deserialisiert".  Beide Flattformen sind little.
1
// Decomposed IEEE 754 single
2
typedef struct
3
{
4
  int sign_bit;
5
  // Mantissa without (23 bits) and with the leading (implicit) 1 (24 bits)
6
  unsigned mant, mant1;
7
  int exp;
8
  int exp_biased;
9
  int fclass;
10
  double x;
11
} avr_float_t
12
13
enum
14
  {
15
    FT_NORM,
16
    FT_DENORM,
17
    FT_INF,
18
    FT_NAN
19
  };
20
21
// IEEE 754 single
22
static avr_float_t
23
decode_avr_float (unsigned val)
24
{
25
  // float =  s  bbbbbbbb mmmmmmmmmmmmmmmmmmmmmmm
26
  //         31
27
  // s = sign (1)
28
  // b = biased exponent
29
  // m = mantissa
30
31
  int one;
32
  const int DIG_MANT = 23;
33
  const int DIG_EXP  = 8;
34
  const int EXP_BIAS = 127;
35
  avr_float_t af;
36
37
  int r = (1 << DIG_EXP) -1;
38
  unsigned mant = af.mant = val & ((1 << DIG_MANT) -1);
39
  val >>= DIG_MANT;
40
  af.exp_biased = val & r;
41
  af.exp = af.exp_biased - EXP_BIAS;
42
43
  val >>= DIG_EXP;
44
  af.sign_bit = val & 1;
45
46
  // Denorm?
47
  if (af.exp_biased == 0)
48
    af.fclass = FT_DENORM;
49
  else if (af.exp_biased < r)
50
    af.fclass = FT_NORM;
51
  else if (mant == 0)
52
    af.fclass = FT_INF;
53
  else
54
    af.fclass = FT_NAN;
55
56
  switch (af.fclass)
57
    {
58
    case FT_NORM:
59
    case FT_DENORM:
60
      one = af.fclass == FT_NORM;
61
      af.mant1 = mant | (one << DIG_MANT);
62
      af.x = ldexp ((double) af.mant1, af.exp - DIG_MANT);
63
      af.x = copysign (af.x, af.sign_bit ? -1.0 : 1.0);
64
      break;
65
    case FT_NAN:
66
      af.x = nan ("");
67
      break;
68
    case FT_INF:
69
      af.x = af.sign_bit ? -HUGE_VAL : HUGE_VAL;
70
      break;
71
    }
72
73
  return af;
74
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.