Forum: Mikrocontroller und Digitale Elektronik gleitkommazahl Wert eingeben durch einzelne Bytes


von Chris T. (chris0086)


Lesenswert?

Hallo Leute,
ich stehe grad vor dem Problem einer Gleitkommazahl einen Wert 
zuzuweisen.
Allerdings möchte ich diesen Wert durch 4 einzelne Bytes zuweisen, da 
ich diese später über die serielle Schniststelle empfangen will.

Also folgender Code ist erstmal vorhanden:
1
double m; //Anstieg Leistunggerade
2
double e_m EEMEM ;    //Anstieg Leistungsgerade EEPROM
3
4
#if (DEBUGLEVEL == 1)
5
     eeprom_busy_wait();
6
7
     m = 0x000042e8;
8
     //m= 116.0;
9
     eeprom_write_block(&m, &e_m ,sizeof(double));
10
     eeprom_busy_wait();
11
     eeprom_write_byte(&e_sal, 0x46);
12
#endif
13
  // Werte aus EEPROM lesen
14
  eeprom_busy_wait();
15
  eeprom_read_block(&m,&e_m, sizeof(double));
16
  eeprom_busy_wait();
17
  sal = eeprom_read_byte(&e_sal);
18
  dtostrf(m,7,4,text);
19
  lcd_puts(text);
20
  USART_SendS(&m,4);

Wennich jetzt meine Wertzuweisung auskommentiere und den Wert m= 116.0 
einkommentiere zeigt mir das Display den Wert 116.0 richtig an und die 
serielle gibt den wert 0000E842 aus.
In der Annahme das dieser Wert der Speichervariablen entspricht habe ich 
veschiedene Kombinationen der Direktzuweisungen ala m= 0x0000E842 
probiert aber dann kommt immer irgendeinw Zahl raus und auch die Ausgabe 
der seriellen Schnittstelle gibt nicht die Werte raus die der Variablen 
über die direkte Zuweisung gegeben wurden.
Wo hab ich nicht zu Ende gedacht?

Wie schon oben geschriebensoll der Wert später über die serielle 
Schnittstelle eingelesen werden und da muss ich wissen was ich genau 
senden muss für korrekte Anzeige des Wertes.

Zum testen hab ich den Wert 116.0 auch hier: 
http://gregstoll.dyndns.org/~gregstoll/floattohex/ eingegeben und in Hex 
umrechnen lassen. Dort kommt dann auch ein Wert mit E842 raus. Also ist 
der Wert wohl nicht ganz falsch...

Wie bekomm ichs nun hin ?

von Peter II (Gast)


Lesenswert?

Chris tian schrieb:
> In der Annahme das dieser Wert der Speichervariablen entspricht habe ich
> veschiedene Kombinationen der Direktzuweisungen ala m= 0x0000E842
> probiert aber dann kommt immer irgendeinw Zahl raus und auch die Ausgabe
> der seriellen Schnittstelle gibt nicht die Werte raus die der Variablen
> über die direkte Zuweisung gegeben wurden.
> Wo hab ich nicht zu Ende gedacht?

 m = 0x000042e8;

das ist eine ganz normale zuweisung da kommt auch genau das raus was 
dort steht. Also bestimmt nicht die 4 bytes.

double d;
uint8_t daten[4];

memcpy( daten, &d, 4 );
oder zurück
memcpy( &d, daten, 4 );

in daten hast du denn die 4 bytes.

von Falk B. (falk)


Lesenswert?

Little Endianess beachtet?

von Chris T. (chris0086)


Lesenswert?

Mit
1
     daten[0]= 0x00;
2
     daten[1]= 0x00;
3
     daten[2]= 0xE8;
4
     daten[3]= 0x42;
5
     //m = 0x000042e8;
6
     memcpy(&m,daten,4);

funktioniert es, Danke
Aber float bzw. Double sind auf dem avr ja 4 byte groß. Wieso kann ich 
nicht direkt die einzelnen bytes per shift zuweisen in etwa so: m = 
(d4<<24)|(d3<<16)|(d2<<8)|(d1);

von Peter II (Gast)


Lesenswert?

Chris tian schrieb:
> m = (d4<<24)|(d3<<16)|(d2<<8)|(d1);

weil du denn einfach die INT zahl zweist und kein float. Es wird also 
nur ein cast gemacht.

von Karl H. (kbuchegg)


Lesenswert?

Chris tian schrieb:

> nicht direkt die einzelnen bytes per shift zuweisen in etwa so: m =
> (d4<<24)|(d3<<16)|(d2<<8)|(d1);

weil da im Grunde auch nichts anderes steht als
1
   m = 5;

nur hast du das eben etwas komplizierter ausgedrückt. Aber im Grunde ist 
das trotzdem nichts anderes als eine Zuweisung eines Zahlwertes an eine 
double Variable. Du würdest dich schön bedanken, wenn ein simples
1
  m = 116.8;
irgendwas anderes machen würde, als in m den Gleitkommawert zu 
speichern, der sich aus dem Auswerten der rechten Seite der Zuweisung 
ergibt.

von Axel S. (a-za-z0-9)


Lesenswert?

Chris tian schrieb:

> Aber float bzw. Double sind auf dem avr ja 4 byte groß. Wieso kann ich
> nicht direkt die einzelnen bytes per shift zuweisen in etwa so: m =
> (d4<<24)|(d3<<16)|(d2<<8)|(d1);

Vielleicht fängst du ja doch besser mit C-Grundlagen an?
Stichwort union


XL

von Peter II (Gast)


Lesenswert?

Axel Schwenke schrieb:
> Stichwort union

nein nicht schon wieder. Wenn du die C-Grundlagen könntest, dann 
müsstest du wissen das unions dafür nicht geeignet sind.

von Axel S. (a-za-z0-9)


Lesenswert?

Peter II schrieb:
> Axel Schwenke schrieb:
>> Stichwort union
>
> nein nicht schon wieder. Wenn du die C-Grundlagen könntest, dann
> müsstest du wissen das unions dafür nicht geeignet sind.

Und wenn du C könntest, dann würde dir klar werden, daß dein 
memcpy(&float_var, char_array_var, 4) bzw. zurück genau das gleiche 
macht, nur an zwei verschiedenen Adressen. Während eine union die 
Daten an der gleichen Adresse wahlweise als float oder als char[] 
interpretiert. Also husch, husch - zurück unter deinen Stein!

@Moderation: und wieder ein Gast, der über die Stränge schlägt. Können 
wir dieses Forum nicht endlich mal für anonyme Trolle schließen? 
BITTE!


XL

von Peter II (Gast)


Lesenswert?

Axel Schwenke schrieb:
> Und wenn du C könntest, dann würde dir klar werden, daß dein
> memcpy(&float_var, char_array_var, 4) bzw. zurück genau das gleiche
> macht, nur an zwei verschiedenen Adressen. Während eine union die
> Daten an der gleichen Adresse wahlweise als float oder als char[]
> interpretiert. Also husch, husch - zurück unter deinen Stein!

nein tut es nicht. Lies dir den C-Standard durch dann wirst du 
feststellen das diese Verhalten nicht definiert ist. Du hast scheinbar 
bis jetzt glücke gehabt das es funktioniert. Bei der nächsten 
Compierversion kann es schon wieder anders aussehen.

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:

> nein tut es nicht. Lies dir den C-Standard durch dann wirst du
> feststellen das diese Verhalten nicht definiert ist. Du hast scheinbar
> bis jetzt glücke gehabt das es funktioniert. Bei der nächsten
> Compierversion kann es schon wieder anders aussehen.


Ich geb dir zwar im Prinzip recht, denn formal hast du recht. Es ist 
tatsächlich im Standard nicht definiert, was bei einer union-Lösung 
rauskommt.

Tatsache ist aber auch, dass hier seit mehr als 35 Jahren die normative 
Kraft des Faktischen greift. Es gab keinen und es gibt keinen 
C-Compiler, bei dem die union Lösung nicht greift. Und das wird sich 
auch nicht ändern. Ein C Compiler, bei dem die union Lösung nicht 
funktioniert, schiesst sich selbst ein Eigentor, für den es keinen Grund 
gibt.
Selbst Mitglieder des ISO-Gremiums benutzen diese Lösung, solange dem 
Benutzer klar ist, dass er sich um die Byte-Order selbst kümmern muss. 
Das Problem an dieser Stelle ist nicht, dass es ein prinzipelles 
technisches Problem gäbe, sondern dass man dieser Lösung keine 
offizielle Absegnung im C-Standard erteilen kann, weil man dann zuviele 
Details der tatsächlichen Implementierung vorschreiben müsste, was man 
nicht will.
Daher ist die union Lösung zwar faktisch ein gangbarer Weg, für den es 
allerdings offiziell keine Absegnung gibt. Aber was viel wichtiger ist: 
es gibt offiziell überhaupt keinen vernünftigen gangbaren Weg mit dem 
man das Problem lösen könnte. Alle Wege führen über undefined behaviour. 
Sei es Pointer umcasten, sei es memcpy. Pointer umcasten mündet 
automatisch in undefined behaviour und bei memcpy ist es die 
Byte-Reihenfolge bzw. das nciht vorgeschriebene Floating Point System, 
welches undefined behaviour hervorruft.

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Alle Wege führen über undefined behaviour. Sei es Pointer
> umcasten, sei es memcpy.

das verhalten von memcpy ist wohl definiert. Bei union kann der optimier 
dazwischenfunken. Er könnte davon ausgehen das der floatwert sich nicht 
geändert hat wenn jemand in den int wert schreibt.

Und wenn selbst MS dazu schreibt:

If a union of two types is declared and one value is stored, but the 
union is accessed with the other type, the results are unreliable. For 
example, a union of float and int is declared. A float value is stored, 
but the program later accesses the value as an int. In such a situation, 
the value would depend on the internal storage of float values. The 
integer value would not be reliable.

dann verwende ich das nicht.

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Karl Heinz Buchegger schrieb:
>> Alle Wege führen über undefined behaviour. Sei es Pointer
>> umcasten, sei es memcpy.
>
> das verhalten von memcpy ist wohl definiert.

Aber es ist nicht definiert was passieren muss, wenn du einen float 
Pointer auf einen void* umcastest und den dann benutzt um mittels memcpy 
an diese Speicheradresse ein paar Bytes reinzuballern.

> Bei union kann der optimier
> dazwischenfunken. Er könnte davon ausgehen das der floatwert sich nicht
> geändert hat wenn jemand in den int wert schreibt.

Den Fall haben wir aber nicht.
In der union stehen 2 Member, deren sizeof exakt gleich groß ist

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Den Fall haben wir aber nicht.
> In der union stehen 2 Member, deren sizeof exakt gleich groß ist

was hat der optimier mit der größe zu tun.
1
union.int = 1
2
union.float = 3.14
3
if ( union.int == 1 ) {
4
  foo();
5
}

hier könnte der Optimierer davon ausgehen das die bedingung war ist. 
Denn der int wird nicht geändert.

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Aber es ist nicht definiert was passieren muss, wenn du einen float
> Pointer auf einen void* umcastest und den dann benutzt um mittels memcpy
> an diese Speicheradresse ein paar Bytes reinzuballern.

die Adresse eines floats ist sehr wohl definiert. Und auch wie die bytes 
im Speicher angelegt sind. (zumindest auf einer Platform/compiler). 
Diese wissen braucht man um soetwas zu machen das ist klar.

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Aber es ist nicht definiert was passieren muss, wenn du einen float
> Pointer auf einen void* umcastest und den dann benutzt um mittels memcpy
> an diese Speicheradresse ein paar Bytes reinzuballern.

nachtrag:

das muss schon definiert sein, sonst könnte man eine structs kopieren.

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Karl Heinz Buchegger schrieb:
>> Den Fall haben wir aber nicht.
>> In der union stehen 2 Member, deren sizeof exakt gleich groß ist
>
> was hat der optimier mit der größe zu tun.

du redest von einer anderen Sache als ich (und der Rest).

Wir reden von
1
union convert
2
{
3
  float   fValue;
4
  uint8_t bValue[ sizeof float ];
5
};

und der Benutzung dieser Union um einen Float auf Byteebene zu befüllen. 
Über bValue alle Bytes besetzen, über fValue den float rausziehen.

Offiziell nicht abgesegnet, aber de facto seit über 35 Jahren für diese 
Zwecke benutzt - und nur für diese Zwecke. Bei anderen union Belegungen 
bzw. Einsatzzwecken kann und wird es Probleme geben.

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Karl Heinz Buchegger schrieb:
>> Aber es ist nicht definiert was passieren muss, wenn du einen float
>> Pointer auf einen void* umcastest und den dann benutzt um mittels memcpy
>> an diese Speicheradresse ein paar Bytes reinzuballern.
>
> nachtrag:
>
> das muss schon definiert sein, sonst könnte man eine structs kopieren.

Das kann nicht definiert sein, weil der Standard keine Aussage darüber 
trifft, welches Floating Point System überhaupt zu benutzen ist. Genauso 
wie 2-er Komplement nicht vorgeschrieben ist. Genau wie Endianess nicht 
vorgeschrieben ist.

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das kann nicht definiert sein, weil der Standard keine Aussage darüber
> trifft, welches Floating Point System überhaupt zu benutzen ist. Genauso
> wie 2-er Komplement nicht vorgeschrieben ist.

genau  das habe ich doch geschrieben, wie die bedeutung der bytes 
Platform und compiler abhängig sind. Aber wie man an die bytes rankommt 
ist definiert.

> du redest von einer anderen Sache als ich (und der Rest).
du weil es alle falsch machen ist es noch lange nicht richtig. Und wenn 
extra angemekert ist das das verhalten nicht definiert ist verlasse ich 
micht bestimmt nicht darauf.

http://msdn.microsoft.com/en-us/library/y9zewe0d.aspx

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:

>> du redest von einer anderen Sache als ich (und der Rest).
> du weil es alle falsch machen ist es noch lange nicht richtig.

Ich denke ich habe es schon angemerkt, dass diese für diese union 
Verwendung die normative Kraft des Faktischen gilt.
Es ist nicht ganz sauber, es gibt Einschränkungen. Aber mit denen kann 
man leben.

> Und wenn
> extra angemekert ist das das verhalten nicht definiert ist verlasse ich
> micht bestimmt nicht darauf.

Der einzige Weg, mit dem du tatsächlich 100% richtig dieses Problem 
lösen könntest, besteht darin, für float das zu programmieren, was man 
bei int mit
1
 unsigned int i = 256 * HighByte + LowByte;
macht. (Bei einem int müsste man da schon wieder die Behandlung eines 
Vorzeichens rausziehen, denn ob 1-er Komplement oder 2-er oder 
vielleicht ganz was anderes ist ja ebenfalls nicht definiert.

Nur das du das für float nicht wirklich machen willst, dir den Float aus 
Mantisse und Exponent und entsprechenden Rechenverfahren ausgehend aus 
den Bytes zusammenzubauen.

Das wäre der einzige Weg, an dem du nirgends im Standard aneckst, alles 
100% sauber ist und der auf allen vergangenen, heutigen und zukünftigen 
Plattformen funktioniert. Den man aber aus naheliegenden Gründen nicht 
geht, wenn es nicht sein muss.


Der Knackpunkt an der ganzen Sache ist immer dann vorhanden, wenn man zb 
eine Variable binär auf ein File schreibt und auf einem anderen System 
wieder einliest. Alles was es dabei zu beachten gilt, ist genau das was 
unspecified ist und was dir auch in diesem speziellen Fall vom Standard 
in die Quere gelegt wird. Eine 100% saubere, standard konforme Lösung 
umschifft all diese Dinge und kann ein binäres File auch dann korrekt 
lesen, wenn das lesende System völlig anders funktioniert als das 
schreibende. Und das lässt nicht besonders viel Spielraum für eine 100% 
saubere Lösung.

von Peter II (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ich denke ich habe es schon angemerkt, dass diese für diese union
> Verwendung die normative Kraft des Faktischen gilt.
> Es ist nicht ganz sauber, es gibt Einschränkungen. Aber mit denen kann
> man leben.

mich wunder ein wenig das für dich eindeutig undefiniertes verhalten, 
was sich bei jeder Compilerversion ändern kann sinnvoll ist.

Ich sehen wirklich das Problem bei der Optimierung. Diese wird immer 
weiter verbessert. Und wenn war mal von cpus mit FPU ausgehen, dann darf 
der compiler den float/double datentype in einem FPU register halten und 
den int wert in einem normales Register. Damit ist für mich klar das 
beiden werte nicht gleichzetig geändert werden müssen.

von Maxx (Gast)


Lesenswert?

Und was machst du, wenn deine compilerchain bei entsprechender 
Optimierungsstufe float nicht mehr als IEEE-754 mit 32 bit 
implementiert? Z.B. als 80 bit float für die FPU? Dann ist dein memcpy 
genauso für die Tonne.

=> undefiniert

Und ob ich undefiniert(1) oder undefiniert(2) nehme ist Wumps

von Peter II (Gast)


Lesenswert?

Maxx schrieb:
> Und was machst du, wenn deine compilerchain bei entsprechender
> Optimierungsstufe float nicht mehr als IEEE-754 mit 32 bit
> implementiert? Z.B. als 80 bit float für die FPU? Dann ist dein memcpy
> genauso für die Tonne.

nein dafür macht man eine prüfung rein.

ifdef 4 <> sizeof( float)

und wie schon mehrfach geschrieben der aufbau vom float muss gekannt 
sein, sonst kann man keinen float zwischen system übertragen. Und dieser 
ändert sich auch nicht spontan.

von Maxx (Gast)


Lesenswert?

Richtig, dann ist essig.

Peter II schrieb:
> Maxx schrieb:
>> Und was machst du, wenn deine compilerchain bei entsprechender
>> Optimierungsstufe float nicht mehr als IEEE-754 mit 32 bit
>> implementiert? Z.B. als 80 bit float für die FPU? Dann ist dein memcpy
>> genauso für die Tonne.
>
> nein dafür macht man eine prüfung rein.
>
> ifdef 4 <> sizeof( float)

+1 für das Bemühen, -1 für die Flüchtigkeit


> und wie schon mehrfach geschrieben der aufbau vom float muss gekannt
> sein, sonst kann man keinen float zwischen system übertragen.

Nein. Genauer: Der Aufbau vom Float-Datentypen muss auf Ziel- und 
Quellsystem gleich sein, sonst ...
Die Struktur muss ich dafür nicht kennen. Das ist ein bedeutender 
Unterschied.

> Und dieser
> ändert sich auch nicht spontan.

Das ist wo festgelegt? Ich sehe keine zwingende Notwendigkeit, dass eine 
Compiler-Einstellung wie -O das nicht ändern dürfte. Wenn ich sie 
übersehe: wo?

von Peter II (Gast)


Lesenswert?

Maxx schrieb:
>> Und dieser
>> ändert sich auch nicht spontan.
>
> Das ist wo festgelegt? Ich sehe keine zwingende Notwendigkeit, dass eine
> Compiler-Einstellung wie -O das nicht ändern dürfte. Wenn ich sie
> übersehe: wo?

nach der Optimierung muss das gleiche Ergebniss für den Anwender 
erscheinen. Wenn sich die genauigkeit vom float ändert, dann ändert sich 
auch das ergebniss. Damit darf sich das nicht ändern.

von Maxx (Gast)


Lesenswert?

Peter II schrieb:
> Wenn sich die genauigkeit vom float ändert, dann ändert sich
> auch das ergebniss. Damit darf sich das nicht ändern.

Nein das ist nicht zwingend. Struktur und Bedeutung sind nicht zwingend 
gekoppelt.

Der Optimierer macht dies mit Ganzzahlen deutlich, indem er z.B. ein 32 
Bit Register für die Speicherung von 8 bit Werten verwendet. Wo die 8 
bit in den 32 verteilt werden ist nicht definiert, nur dass nach der 
Rechnung das gleiche darsteht.

Du siehst also: die Struktur der Speicherung eines Datentyp kann sich 
wandeln, ohne dass sich seine Semantik ändert.

von Peter II (Gast)


Lesenswert?

Maxx schrieb:
> Der Optimierer macht dies mit Ganzzahlen deutlich, indem er z.B. ein 32
> Bit Register für die Speicherung von 8 bit Werten verwendet. Wo die 8
> bit in den 32 verteilt werden ist nicht definiert, nur dass nach der
> Rechnung das gleiche darsteht.
>
> Du siehst also: die Struktur der Speicherung eines Datentyp kann sich
> wandeln, ohne dass sich seine Semantik ändert.

Register gib es in C nicht. Und es gibt auch keine Zeiger auf register. 
Es gibt variabeln und dort ist wohl bekannt wie die bytes in einen float 
angelegt sind. und diese ändert sich nicht mit der optimierun.

von Maxx (Gast)


Lesenswert?

Peter II schrieb:
dert.
>
> Register gib es in C nicht.

Aber auf dem System. Ich kann auch sagen er speichert es in 4 Byte 
Zellen ab. Das macht keinen Unterschied. Es sit lediglich Nomenklatur 
auf dem Zielsystem. Du wirst auch bemerken, dass ich vom Optimizer und 
nicht von C gesprochen habe. Ein Optimizer ist zwingend am System 
gebunden.

> Und es gibt auch keine Zeiger auf register.

Och, dann lass dich mal von den AVR oder '51 Programmierer belehren.

> Es gibt variabeln und dort ist wohl bekannt wie die bytes in einen float
> angelegt sind.

Nein.
Wo soll das bitte definiert sein?

> und diese ändert sich nicht mit der optimierun.

Das ist nicht definiert. Ob es sich ändert oder nciht ändert an der 
Undefiniertheit nicht.

Deine Lösung ist genauso abhängig von Vermutungen die nicht zwingend 
sind wie die Andere.

von Peter II (Gast)


Lesenswert?

Maxx schrieb:
> Deine Lösung ist genauso abhängig von Vermutungen die nicht zwingend
> sind wie die Andere.

nein. der float datentype ist in der doku vom compiler festgeschrieben. 
Und an diesen kann man sich sehr wohl halten. Genauso wie festgelegt ist 
ob in char sign oder unsignd ist. Das ist nicht überall gleich aber sehr 
wohl für einen compiler/Platform festgelegt. Das man es sogar eventuell 
umschalten kann ist auch klar. Aber es ändert sich nicht mit der 
optimierung.

von Maxx (Gast)


Lesenswert?

Peter II schrieb:
> Maxx schrieb:
>> Deine Lösung ist genauso abhängig von Vermutungen die nicht zwingend
>> sind wie die Andere.
>
> nein. der float datentype ist in der doku vom compiler festgeschrieben.
> Und an diesen kann man sich sehr wohl halten. Genauso wie festgelegt ist
> ob in char sign oder unsignd ist. Das ist nicht überall gleich aber sehr
> wohl für einen compiler/Platform festgelegt. Das man es sogar eventuell
> umschalten kann ist auch klar. Aber es ändert sich nicht mit der
> optimierung.

Und dann war nochmal das Problem mit dem union?

Erinner dich: du hast Jehova gerufen, weil es nicht wie im C Standard 
definiert verwendet worden ist, sondern nur compiler-spezifisch. Und 
jetzt argumentierts du deine Lösung ist besser, weil es nicht wie im C 
Standard definiert verwendet worden ist, sondern nur 
compiler-spezifisch.

von Peter II (Gast)


Lesenswert?

Maxx schrieb:
> Und dann war nochmal das Problem mit dem union?

das es schon so definiert ist das das verhalten undefinert ist. Es steht 
extra geschrieben das man es so nicht verwenden darf!

von Axel S. (a-za-z0-9)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Der einzige Weg, mit dem du tatsächlich 100% richtig dieses Problem
> lösen könntest, besteht darin, für float das zu programmieren, was man
> bei int mit unsigned int i = 256 * HighByte + LowByte;
> macht. (Bei einem int müsste man da schon wieder die Behandlung eines
> Vorzeichens rausziehen, denn ob 1-er Komplement oder 2-er oder
> vielleicht ganz was anderes ist ja ebenfalls nicht definiert.
>
> Nur das du das für float nicht wirklich machen willst, dir den Float aus
> Mantisse und Exponent und entsprechenden Rechenverfahren ausgehend aus
> den Bytes zusammenzubauen.
>
> Das wäre der einzige Weg, an dem du nirgends im Standard aneckst, alles
> 100% sauber ist und der auf allen vergangenen, heutigen und zukünftigen
> Plattformen funktioniert.

Alles ACK bis auf den letzten Satz. Weil man eine solche händische 
Zerlegung des Float-Werts ja für genau ein Float-Format programmiert. 
Das heißt man kann zwar für jede Architektur eine solche Routine 
schreiben, aber es ist nicht immer die gleiche.

Der Standard verbietet übrigens keineswegs, eine union mit einem Typ 
zu beschreiben und dann mit dem anderen auszulesen. Undefiniert ist 
lediglich, was man beim Auslesen des längeren Typs bekommt, wenn man 
vorher einen kürzeren reingeschrieben hat. Dann sind genau die Bytes 
undefiniert, die der kürzere Typ nicht verwendet.

Ach ja. Gegen das wegoptimieren von Zugriffen hilft wie immer in solchen 
Fällen ein strategisch plaziertes volatile


XL

von Axel S. (a-za-z0-9)


Lesenswert?

Peter II schrieb:

> wenn
> extra angemekert ist das das verhalten nicht definiert ist verlasse ich
> micht bestimmt nicht darauf.
>
> http://msdn.microsoft.com/en-us/library/y9zewe0d.aspx

Das Verhalten ist nicht undefiniert (zumindest nicht in dem Sinn wie 
der C Standard dieses Wort verwendet). Laß es mich nochmal zitieren:

> If a union of two types is declared and one value is stored,
> but the union is accessed with the other type, the results
> are unreliable.

Edit: Einschub

Also nicht undefined sondern unreliable = unzuverlässig. Was sie 
damit meinen erklären sie gleich selber:

> For example, a union of float and int is declared. A float
> value is stored, but the program later accesses the value
> as an int. In such a situation, the value would depend on
> the internal storage of float values.

Der ausgelesene int Wert ist also sehr wohl deterministisch, aber er 
hängt von der internen Darstellung des float Typs ab (wer konnte damit 
rechnen!). Aber so lange man sich sicher sein kann, daß sich das float 
Format zwischen zwei derartigen Zugriffen nicht ändert, ist das 
vollkommen unerheblich.

Und das Vorhaben des TE ist genau ein solcher Fall. Er will einen 
float-Wert aus seinem AVR-Programm byteweise über eine serielle 
Schnittstelle schicken und später den gleichen Wert über die serielle 
wieder in das Programm zurück schaffen (auf dem Umweg über das EEPROM). 
Es ist nicht anzunehmen daß sich das Fließkommaformat seines Binaries 
zwischen diesen beiden Schritten verändern wird.


XL

von Karl H. (kbuchegg)


Lesenswert?

Maxx schrieb:

> Erinner dich: du hast Jehova gerufen, weil es nicht wie im C Standard
> definiert verwendet worden ist, sondern nur compiler-spezifisch. Und
> jetzt argumentierts du deine Lösung ist besser, weil es nicht wie im C
> Standard definiert verwendet worden ist, sondern nur
> compiler-spezifisch.

Genau darum gehts.
Man kann nicht jemanden verteufeln, weil er eine union Lösung anbietet 
und dann im Gegenzug ein
1
  float f;
2
  unsigned char b[sizeof(float)];
3
4
  memcpy( &f, b, sizeof(float);

oder ein
1
  float f;
2
  unsigned char b[sizeof(float)];
3
4
  f = *(float*)b;

anbieten.
Alle 3 Lösungen haben exakt die gleichen Probleme. Alle 3 Lösungen sind 
aus genau den gleichen Gründen unspecified. Wenn auch der genaue Text 
und die genaue Begründung im Falle einer union Lösung etwas anders 
formuliert ist. Aber im Kern kranken alle 3 Lösungen an genau demselben 
Problem und es ist genau dieses Problem, welches für unions zu einem 
lapidaren "Du darfst nur über denselben Member auslesen über welchen 
auch geschrieben wurde" zusammengefasst wurde.
Der Standard kann und will keine byteübergreifenden Vorschriften für 
bestimmte Datentypen machen.
Was aber sehr wohl bei einer union definiert ist, ist das alle Member 
sich auf denselben Speicherbereich beziehen, wobei der längste Member 
den Speicherverbrauch der union diktiert. Sind alle Member gleich lang, 
dann teilen sich alle Member ein und denselben Speicher. Das IST 
definiert. Was nicht definiert ist, welche Zusammenhänge es auf Bytebene 
zwischen diesen Member existieren. Aber dieses ist hier
  memcpy( &f, b, sizeof(float);
und hier
  f = *(float*)b;
aus genau den gleichen Gründen genausowenig definiert.

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Karl Heinz Buchegger schrieb:
>> Ich denke ich habe es schon angemerkt, dass diese für diese union
>> Verwendung die normative Kraft des Faktischen gilt.
>> Es ist nicht ganz sauber, es gibt Einschränkungen. Aber mit denen kann
>> man leben.

Es ändert sich eben NICHT.
Es hat sich die letzten 35 Jahre nicht geändert und es wird sich auch in 
Zukunft nicht ändern. Es gibt auch keinen Grund, warum sich das ändern 
sollte.

mittels
1
union {
2
  T  member1;
3
  unsigned char member2[ sizeof member1 ];
4
};
konnte, kann und wird man auf die Bytes eines Datentyps T zugreifen 
können.

von Karl H. (kbuchegg)


Lesenswert?

Axel Schwenke schrieb:
> Karl Heinz Buchegger schrieb:
>> Der einzige Weg, mit dem du tatsächlich 100% richtig dieses Problem
>> lösen könntest, besteht darin, für float das zu programmieren, was man
>> bei int mit unsigned int i = 256 * HighByte + LowByte;
>> macht. (Bei einem int müsste man da schon wieder die Behandlung eines
>> Vorzeichens rausziehen, denn ob 1-er Komplement oder 2-er oder
>> vielleicht ganz was anderes ist ja ebenfalls nicht definiert.
>>
>> Nur das du das für float nicht wirklich machen willst, dir den Float aus
>> Mantisse und Exponent und entsprechenden Rechenverfahren ausgehend aus
>> den Bytes zusammenzubauen.
>>
>> Das wäre der einzige Weg, an dem du nirgends im Standard aneckst, alles
>> 100% sauber ist und der auf allen vergangenen, heutigen und zukünftigen
>> Plattformen funktioniert.
>
> Alles ACK bis auf den letzten Satz. Weil man eine solche händische
> Zerlegung des Float-Werts ja für genau ein Float-Format programmiert.
> Das heißt man kann zwar für jede Architektur eine solche Routine
> schreiben, aber es ist nicht immer die gleiche.

Exakt.
Genau das gleiche hat man ja auch bei einem 2-Byte int.
Ich muss a priori wissen, welches das High-Byte und welches das Low-Byte 
ist, welche ich zu einem 2-Byte int zusammensetze.
Hier bei float ist das auch nicht anders. Ich muss wissen, wo die Bytes 
herkommen, wie sie von dieser Quelle zu interpretieren sind, welche die 
Mantissenbits sind und welche der Exponent, wie das Floating Point 
System der Quelle funktioniert und erst dann kann ich die Quellbytes 
zerlegen und auf dem Zielsystem eine Funktion schreiben, die mit den 
Floating Point Routinen des Zielsystems einen auf diesem Zielsystem 
gültigen float zusammenbaut, der den gleichen Zahlenwert hat, wie der 
float auf dem Quellsystem. Diese Routine hängt dann perverser weise 
nicht mehr von der float-Implementierung des Zielsystems ab, aber sehr 
wohl von der Float-Implementierung des Quellsystems.
Das dieses komplex und aufwändig ist, ist unbestritten. Aber es ist die 
Analogie zu dem, was ich tun muss um mittels
1
   unsigned int i = HighByte * 256 + LowByte;
einen int auf dem Zielsystem zusammenzubauen, wenn ich weiß, welches der 
beiden Quellbytes auf dem Quellsystem das Highbyte und welches das 
Lowbyte war. Wie das Zielsystem dann seine int aufbaut, welche Endianess 
vorliegt braucht mich dann nicht mehr zu kümmern. Aber vom Quellsystem 
muss ich das wissen.

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.