PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Ersten 8 bit einer 32 bit Zahl auslesen



C_Classic
22.05.2010, 16:51
Hey,
ich schreibe gerade eine Funktion, um ein EEPROM anzusteuern. Dieses hat 18 Adressleitungen. Nun möchte ich meiner Funktion einfach eine Zahl übergeben, die dann auf die 3 Ports aufgeteilt wird.
Dazu muss ich die große Zahl (momentan 32 bit) in 3 kleine 8-Bit-Zahlen aufteilen. Wie kann man so etwas machen?

MfG C_Classic

Dirk
22.05.2010, 17:02
...32 bit) in 3 kleine 8-Bit-Zahlen aufteilen. Wie kann man so etwas machen?
... z.B. mit einer union, in der mittels struct die 3 Bytes definiert werden.

Gruß Dirk

C_Classic
22.05.2010, 17:10
Könntest du mir bitte erklären, wie diese union denn aussehen muss... hab noch nie damit gearbeitet.

BurningWave
22.05.2010, 17:31
3*8 gibt aber nicht 32. Deswegen brauchst du doch 4 Teile und nicht 3?!

Du kannst das z.B. so machen (ich hab es nicht probiert aber theoretisch müsse es doch so funktionieren?):


struct SFloat {
char parts[4];
};

SFloat zahl2;
float zahl1;
zahl2 = (SFloat)(zahl1);

C_Classic
22.05.2010, 17:46
3*8 gibt aber nicht 32.
Ja, aber ich hab 18 Adressleitungen und 32Bit ist die nächst-größte Bit-Zahl, die der GCC unterstützt. Deshalb reichen mir 3 Teile.

Wie kann ich jetzt bei deiner Variante auf die einzelnen Teile zugreifen?

markusj
22.05.2010, 17:49
Nimm lieber sowas:

output = (input >> (byte * 8)) & 0xFF;
input: 32-Bit Eingabewert
output: 8-Bit Ausgabewert
byte: Index des Bytes (0 .. 3)

Am besten Funktioniert das ganze wenn "byte" eine Konstante ist, dann pickt sich der GCC einfach das passende Byte aus dem (u)int32_t raus.

mfG
Markus

PS: Type-Punning mit Unions sollte man vermeiden

BurningWave
22.05.2010, 17:56
Wie kann ich jetzt bei deiner Variante auf die einzelnen Teile zugreifen?

Falls meine Variante funktioniert, dann so:
zahl2.parts[x]

x muss dabei zwischen 0 und 3 liegen.

mare_crisium
22.05.2010, 17:57
C_Classic,

eine 32-Bit Zahl, die grösser als 16.777.215 ist, kann man nur in vier 8-Bit Zahlen aufteilen - drei 8-Bit Zahlen reichen gerademal für 24 Bit ;-).

Die Umwandlung in einzelne 8-Bit Zahlen (also Bytes) geht so: Dividiere die Originalzahl durch 256. Der Divisionsrest ist die erste 8-Bit Zahl. Wenn das Ergebnis dieser Division grösser als Null ist, dann dividierst Du es wieder durch 256 und nimmst den Divisionsrest als zweite 8-Bit Zahl usw., bis das Divisionsergebnis gleich Null ist.

Dieser Algorithmus gilt alle Zahlen, egal wieviel Bit sie haben :-).

Ciao,

mare_crisium

BurningWave
22.05.2010, 18:07
Type-Punning mit Unions sollte man vermeiden

Warum solte man das vermeiden? Das ist 1. praktisch und 2. braucht man nicht doppelt so viel Speicher.

markusj
22.05.2010, 19:14
Weil Unions nicht dafür gemacht sind, "a" reinzustecken und "b" rauszuholen.
Das kann funktionieren, muss es aber nicht, das Verhalten ist für diesen Fall unspezifiziert!
http://en.wikipedia.org/wiki/Type_punning#cite_note-0

mfG
Markus

PS: Wenn du castest brauchst du auch keinen doppelten Speicher - und bei meiner Variante ebenfalls nicht

BurningWave
22.05.2010, 19:27
Weil Unions nicht dafür gemacht sind, "a" reinzustecken und "b" rauszuholen.

Naja auf jeden Fall funktioniert es.



Wenn du castest brauchst du auch keinen doppelten Speicher - und bei meiner Variante ebenfalls nicht

Deine Methode ist sicher am edelsten, aber zum Verstehen am schwersten.
Ein Nachteil ist aber auch, dass sie nicht mit float, sondern nur mit int, int32_t und so was funktioniert, weil float keine Bitoperationen zulässt.
Ich würde C_Classic diese Methode aber trotzdem empfehlen, solange er kein float hat.

C_Classic
22.05.2010, 20:01
C_Classic,

eine 32-Bit Zahl, die grösser als 16.777.215 ist, kann man nur invier 8-Bit Zahlen aufteilen - drei 8-Bit Zahlen reichen gerademal für 24 Bit ;-).

Die Umwandlung in einzelne 8-Bit Zahlen (also Bytes) geht so: Dividiere die Originalzahl durch 256. Der Divisionsrest ist die erste 8-Bit Zahl. Wenn das Ergebnis dieser Division grösser als Null ist, dann dividierst Du es wieder durch 256 und nimmst den Divisionsrest als zweite 8-Bit Zahl usw., bis das Divisionsergebnis gleich Null ist.

Dieser Algorithmus gilt alle Zahlen, egal wieviel Bit sie haben :-).

Ciao,

mare_crisium

Diese Version hört sich gut an, werds dann morgen mal probieren.



eine 32-Bit Zahl, die grösser als 16.777.215 ist, kann man nur invier 8-Bit Zahlen aufteilen - drei 8-Bit Zahlen reichen gerademal für 24 Bit ;-).
Ist mir schon klar, aber mir reichen ja auch 24 bit. eigentlich brauch ich ja nur 18.

Dirk
22.05.2010, 20:38
@C_Classic:

Könntest du mir bitte erklären, wie diese union denn aussehen muss... hab noch nie damit gearbeitet.
Könnte so aussehen:
typedef union {
uint32_t bits;
struct {
uint8_t byte_1;
uint8_t byte_2;
uint8_t byte_3;
uint8_t byte_4;
};
} fourbytes_t;
Danach kann man eine Variable var32 so deklarieren:
fourbytes_t var32;
Auf die einzelnen Bytes kann man so zugreifen (uint8_t var8;):
Bytes auslesen: var8 = var32.byte_x;
Bytes schreiben: var32.byte_x = var8;

Gruß Dirk

markusj
22.05.2010, 20:53
Naja auf jeden Fall funktioniert es.
Weil GCC laut Dokumentation sich wie erwartet verhält. siehe Dokumentation (http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Optimize-Options.html#index-fstrict_002daliasing-721)


Ein Nachteil ist aber auch, dass sie nicht mit float, sondern nur mit int, int32_t und so was funktioniert, weil float keine Bitoperationen zulässt.
Ist ein Cast auf einen entsprechenden Integertypen so schlimm?

mfG
Markus

EDIT: "GCC" aus dem Linktext entfernt, das Forum macht den Link sonst kaputt

BurningWave
22.05.2010, 23:04
Ist ein Cast auf einen entsprechenden Integertypen so schlimm?

Ich wusste nicht, dass das funktioniert, weil float das mit den Kommastellen und dem Bit für Vorzeichen (also negativ / positiv) ja etwas anders handhabt, wie ein int32_t (das ja gar keine Nachkommastellen haben kann), oder?

markusj
23.05.2010, 00:06
Nach weiterer Recherche und einigen Experimenten mit GCC ziehe ich meine Aussage zum Cast zurück und behaupte das Gegenteil, natürlich kannst du float nicht auf Integer casten ohne dass dabei eine Typumwandlung oder abgrundtief verbotenes Type-Punning über Pointer stattfindet. ;)

Ich bin auf einen sehr guten Artikel (http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html) zu dem Thema gestoßen, der die ganze Problematik sehr gründlich untersucht.
Ebenso wie hier (http://cocoawithlove.com/2008/04/using-pointers-to-recast-in-c-is-bad.html) kommt man zu dem Ergebnis, dass Type-Punning über Unions nicht wirklich dem Standard entspricht, aber unter GCC das Mittel der Wahl ist, um Daten ohne Typumwandlung zu reinterpretieren.
Unter letzerem Link ist übrigens auch ein Makro zu finden, dass das ganze vereinfachen soll, eine weitere Möglichkeit besteht darin, den Speicherbereich (via memcpy oder von Hand) zu kopieren, GCC sollte das gewünschte Vorhaben dann auch erkennen.

Ich fasse die gefundenen Ergebnisse noch einmal kurz zusammen:
Type-Punning über Unions ist im C-Standard undefiniert, über Pointertrickserei à la *(uint32_t *) &floatvariable sogar (strikt) verboten.
GCC definiert, um solche Operationen zuzulassen, dass Unions (wie man es eigentlich erwarten würde) "a rein, b raus" ermöglichen.
Aktiviert man den Compilerschalter "-fno-strict-aliasing" hat man den ganzen Stress auch nicht, dadurch werden aber einige Optimierungen unmöglich.

mfG
Markus

PS: Die Ursache des ganzen liegt in der Spezifikation von C(99), die verbietet dass zwei Pointer unterschiedlichen Typs beim dereferenzieren auf das gleiche Objekt verweisen.