C - Verwendung von Union und binäre Darstellung?

2 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Die Funktion toBinary( ) tut an sich, hat aber arge Probleme mit Überläufen.

Bis zu einer Länge von 8 Bit (0xff) kommt noch das richtige raus, mit 12 Bit (0xfff) schon nicht mehr. Das liegt ganz einfach daran, dass die ints bei der Berechnung überlaufen. 10^10 > 2^32, d.h. bei 10 Bit knallt's schon, wenn man 10x fak mit 10 multipliziert.

Wenn man aus den ints longs macht geht's ein wenig länger gut, reicht aber noch lange nicht für 32 Bit.

Sinniger wäre, das Ergebnis textuell in einem char[ ] (Zeichenkette) zu halten. Oder überhaupt nicht zu halten, sondern direkt rauszuschreiben (tut das printf am Ende ja ohnehin, wieso also nicht gleich ziffernweise??).

Ob man dann mittels Modulo und Division oder mit Rightshifts und Bit-Ands arbeitet ist einerlei. Letzteres ist aber sauberer und ggf. (bei schlechten Compilern) auch performanter.


Slevi89 
Fragesteller
 25.11.2019, 09:33

Okay deine Argument hab ich soweit verstanden. Danke! Die Ausgabe allerdings noch nicht. Wie würde ich das also umschreiben?

0
ArchEnema  25.11.2019, 09:37
@Slevi89

Na ganz simpel:

void toBinary(int zahl) {
  int rest = 0;

  while(zahl) {
    rest = zahl%2;
    zahl = zahl/2;
    printf("%d", rest);
  }
  printf("\n");
}
1
ArchEnema  25.11.2019, 09:41
@ArchEnema

Und statt

rest = zahl % 2;
zahl = zahl / 2;

würde ich

rest = zahl & 1;
zahl = zahl >> 1;

schreiben.

Ist mathematisch in diesem Fall natürlich das selbe: Rechtsshift = geteilt durch 2 bzw. unterstes Bit = gerade/ungerade = Modulo 2.

1
ArchEnema  25.11.2019, 09:44
@ArchEnema

Kann man natürlich noch weiter vereinfachen/aufräumen, ggf. um den Preis der Lesbarkeit. Wir vergeben hier ja keine Preise für das kürzeste C-Programm. ;-))

1
Slevi89 
Fragesteller
 25.11.2019, 09:48
@ArchEnema

Vielen Dank. Die Ausgabe sieht nun so aus:

0000000000000000001100000000001
1074528256
3.5

Die erste Zeile sollte nun die binäre Darstellung sein. Warum sind die beiden Anteile? Also 00000000000000000011 für die 3

und 00000000001 für die 0.5 unterschiedlich lang?

und 1074528256 ist dann die interne Darstellung von 3.5?

und eine weitere Frage zum ursprünglichen Programm:
Was soll dieser ganze Teil mit "fak" eigentlich? Also

erg = erg + rest*fak;

fak = fak10;

Vielen Dank :)

0
ArchEnema  25.11.2019, 10:01
@Slevi89

Das "fak" war der "Zeiger" auf die Dezimalstelle. x 10 -> eine Dezimalstelle weiter. Also im Grunde das dezimale Äquivalent zu Bitshift.

Die Aushabe haut nicht hin, weil die führenden Nullen fehlen. Die "zahl" wird ja immer halbiert, bis nur noch Nullen kommen. Das klappt so natürlich nicht, weil führende (und nachfolgende) Null-Bits keine Ausgabe erzeugen.

Deshalb sollte man besser ein einzelnes 1-Bit über alle Stellen (Bit 32 bis Bit 1) rennen lassen, Bit-And machen, und dann 0 oder nicht-0 in ein 32 chars langes Array schreiben (als '0' oder '1').

1
Slevi89 
Fragesteller
 25.11.2019, 10:21
@ArchEnema

Okay den Teil nach fak hab ich nicht verstanden. Ist die Ausgabe nun falsch, die ich geschrieben habe?

0
Slevi89 
Fragesteller
 25.11.2019, 10:29
@Slevi89

Könntest du bitte einmal gecodet schreiben wie du das meinst?

0
ArchEnema  25.11.2019, 10:35
@Slevi89

Naja, was heißt "falsch"... Sagen wir mal so: Sie gibt nicht das aus, was du denkst bzw. gerne hättest, dass ausgegeben wird. :D

Als Denkansatz:

char bits[33];
bits[32] = 0; // nullterminierter String der Länge 32
for (int i = 0; i < 32; ++i) {
  int digit = (zahl >> i) & 1; // Bit an Position i
  bits[31-i] = digit ? '1' : '0';
}
printf("%s\n", bits);

Oder so ähnlich.

0
ArchEnema  25.11.2019, 10:39
@ArchEnema

Also als toBinary().

Ausgabe dann für

toBinary(0xff);
toBinary(0xfff);
toBinary(0xffff);
toBinary(0xfffff);
toBinary(0xffffff);

Tut m.E. was es soll:

00000000000000000000000011111111
00000000000000000000111111111111
00000000000000001111111111111111
00000000000011111111111111111111
00000000111111111111111111111111
1
Slevi89 
Fragesteller
 25.11.2019, 10:46
@ArchEnema

Ich habe den Code mal abgeändert, aber die Ausgabe ist immer noch "fast" die gleiche. Jetzt bekomme ich:

00000000000000000011000000000010
1074528256
3.500000

anstelle

0000000000000000001100000000001
1074528256
3.5

union zahl { int x[2]; double y; }n;

void toBinary(int zahl){
char bits[33];
bits[32] = 0;
for (int i = 0; i < 32; ++i) {
 int digit = (zahl >> i) & 1;
 bits[31-i] = digit ? '1' : '0';
 printf("%d", digit);
}
 printf("\n");
}
int main(void) {
int erg =0;
sscanf("3.5", "%lf", &n.y);
toBinary(n.x[1]);
printf("%d\n", n.x[1]);
printf("%f\n", n.y);
}

0
ArchEnema  25.11.2019, 11:39
@Slevi89

Klar, du gibst ja nach wie vor "digit" aus.

Gib mal lieber "bits" aus (nach der Schleife).

1
Slevi89 
Fragesteller
 25.11.2019, 14:01
@ArchEnema

dann ist das hier meine erste Ausgabe:

``````````````````````````````````````````````````````````00000000011000000000000000000`000000000011000000000000000000`100000000001100000000000000000001000000000011000000000000000000

wie kann ich das interpretieren? :D

0
Slevi89 
Fragesteller
 25.11.2019, 14:05
@Slevi89

Sorry. Das hier:

dann ist das hier meine erste Ausgabe:

also nach der Schleife bekomme ich dann

01000000000011000000000000000000

1074528256

3.500000

im Prinzip also nur verdreht

0
Slevi89 
Fragesteller
 25.11.2019, 14:09
@Slevi89

#include <stdio.h>

union zahl { int x[2]; double y; }n;

void toBinary(int zahl){

char bits[33];

bits[32] = 0; // nullterminierter String der Länge 32

for (int i = 0; i < 32; ++i) {

 int digit = (zahl >> i) & 1; // Bit an Position i

 bits[31-i] = digit ? '1' : '0';

 // printf("%s", bits);

}

 printf("%s", bits);
 printf("\n");

}

int main(void) {

int erg =0;

sscanf("3.5", "%lf", &n.y);

toBinary(n.x[1]);

printf("%d\n", n.x[1]);

printf("%f\n", n.y);

}

0
Slevi89 
Fragesteller
 25.11.2019, 14:18
@ArchEnema

Super! Vielen Dank, ich suche auch schon die ganze Zeit nach einer bit Darstellung.

Aber nochmal 32 Nullen weil du (also der Link) ja eine 64bit Version hast oder? :)

Warum hat denn das Programm davor das Ergebnis umgedreht gezeigt?

0
ArchEnema  25.11.2019, 14:32
@Slevi89

Weil du vmtl. den Code halb geändert hattest, so dass er vom niedrigen zum höherwertigen Bit durchgelaufen ist und das in genau dieser Reihenfolge auch rausgeschrieben hat.

Deswegen ja bits[31-i], damit der String in Big Endian Reihenfolge rauskommt (d.h. höherwertigste Ziffer links, wie man es von Dezimalzahlen gewohnt ist).

Nicht nur ich/der Link hat 64 bit. Du hast ja auch ein int-Array der Länge 2. Double ist jedenfalls doppelt so lang wie int (auch wenn erstmal unklar ist wie lang der int ist: früher 32 bit, heute vmtl. eher 64 bit).

0
Slevi89 
Fragesteller
 25.11.2019, 14:39
@ArchEnema

Ja es kam falsch herum raus, weil ich quasi jedes Bit rausschreibe.

dann liegt es ggf an der Darstellung meines compilers das er mir nur 32bit im Gesamtergebnis ausspuckt, bzw die restlichen Nullen abschneidet, wobei die Schleife ja auch nur 32bit zählt.

Dennoch: Die Zahl 1074528256

Ist das eine interne Darstellung? oder einfach nur der Wert der Adresse? Sprich was in x[2] steht.

Sorry für die restlichen "Kleinviehfragen".

Dickes Dankeschön nochmal! :)

0
ArchEnema  25.11.2019, 14:41
@ArchEnema

Der Länge des Exponenten nach (augenscheinlich ja 11 Bit)

01000000000011000000000000000000...

handelt es sich um IEEE Doubles (64 Bit).

Wieviel Bits dein "int" hat ist compilerabhängig, kannst du mittels

sizeof(int)

abfragen. Daran solltest du die Schleifenlänge festmachen, wenn es denn portabel sein soll...

0

Ersetze mal die Zeilen

toBinary(n.x[1]);

puts(" ");

printf("%d\n", n.x[1]);

printf("%f\n", n.y)

durch

printf( "%lf = 0x%08x%08x\n", n.y, n.x[0], n.x[1] );

Die Methode toBinary() kann dann raus. Ich gebe die Zahlen hexadezimal aus. Aus jeder Ziffer lassen sich mit etwas Übung oder einen kleinen Umsetzungstabelle die Bits ablesen:

0 = 0000

1 = 0001

...

9 = 1001

A = 1010

...

F = 1111