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
doublem;//Anstieg Leistunggerade
2
doublee_mEEMEM;//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 ?
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.
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);
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.
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
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.
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
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.
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.
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.
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
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.
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.
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.
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
unionconvert
2
{
3
floatfValue;
4
uint8_tbValue[sizeoffloat];
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.
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.
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
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
unsignedinti=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.
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.
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
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.
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?
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.
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.
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.
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.
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.
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.
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!
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
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
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
floatf;
2
unsignedcharb[sizeof(float)];
3
4
memcpy(&f,b,sizeof(float);
oder ein
1
floatf;
2
unsignedcharb[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.
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
Tmember1;
3
unsignedcharmember2[sizeofmember1];
4
};
konnte, kann und wird man auf die Bytes eines Datentyps T zugreifen
können.
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
unsignedinti=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.