Forum: Mikrocontroller und Digitale Elektronik Bei einer Zahl die Bits untersuchen, AVR-Stud. in C


von fragendermensch (Gast)


Lesenswert?

Hallo,

habe folgendes Problem:

uint32_t zahl = 2863311530;

Ich möchte die Variable zahl Bit für Bit untersuchen, ob das jeweilige 
Bit eine 0 oder eine 1 ist.

Wahrscheinlich geht das mit einer "Bitschieberoperation" ganz einfach.
Man schiebt einfach die Zahl "1 hoch Bitstelle" mit einer 
UND-Verknüpfung über die Bits 0 bis 31 und erhält für jede Bit-Stelle 
eine 0 oder 1 als Ergebnis.


So weit, so gut, aber wie wird das in der Praxis geschrieben?


for (i = 0; i< 33; i++)
{
b = (Zahl & (2 hoch i));

ergebnis_verwursten (uint_t b);
}



Es gibt ja die Befehle << und >>, damit geht es sicher eleganter, zumal 
in C ja kein Potenzoperator ohne weiteres zur Verfügung steht.

(gibt es den Variablentyp uint1_t eigentlich?)

Viele Grüße und auf jeden Fall schon mal Danke fürs Lesen!

von Uwe (de0508)


Lesenswert?

Hallo fragendermensch,

ich würde nur das Bit 0 der Zahl testen:
1
if ( Zahl & (1ul<<0) ) { .. }

und dann die Zahl nach rechts Schieben
1
zahl = zahl >> 1ul; // lange Schreibweise
2
// oder
3
zahl >>= 1ul;
4
// oder
5
zahl = zahl / 2;
6
// oder
7
zahl /= 2;

Mehr steht wie immer in dem C Buch:

http://de.wikipedia.org/wiki/The_C_Programming_Language

von Karl H. (kbuchegg)


Lesenswert?

fragendermensch schrieb:

>
> for (i = 0; i< 33; i++)
> {
> b = (Zahl & (2 hoch i));
>
> ergebnis_verwursten (uint_t b);
> }
>
>
>
> Es gibt ja die Befehle << und >>, damit geht es sicher eleganter, zumal
> in C ja kein Potenzoperator ohne weiteres zur Verfügung steht.


    1 << i

IST die Operation '2 hoch i'

2 hoch 0 ->  1         1 << 0   ->  1
2 hoch 1 ->  2         1 << 1   ->  2
2 hoch 2 ->  4         1 << 2   ->  4
2 hoch 3 ->  8         1 << 3   ->  8
2 hoch 4 -> 16         1 << 4   -> 16
...

Aber eigentlich will man auf einem AVR die Operation '1 << i' nicht 
machen, weil dem AVR die dazu notwendige Hardware-Ausstattung fehlt. 
Daher macht man was anderes: Wenn der Berg nicht zum Propheten kommt, 
dann muss eben der Prophet zum Berg.

Einigen wir uns auf das unterste Bit einer Zahl. Dieses Bit kann man 
leicht extrahieren :-)

       x  & 0x01

soweit so gut. Was bringt das? Nun ganz einfach, nachdem dieses Bit 
verwurschtet wurde, schiebt man einfach die Zahl x um eine binäre Stelle 
nach RECHTS, so dass jetzt das ursprüngliche Bit 1 an die unterste 
STelle kommt, wo man es mit dem & wieder leicht extrahieren kann. Und 
dann schiebt man x wieder um 1 Stelle nach rechts, wodurch das 
ursprüngliche Bit 2 an die unterste Stelle zu liegen kommt. etc. etc.

   for( i = 0; i < 32; i++ )
   {
     mach was mit ( x & 0x01 );
     x >>= 1;
   }


das ist nur eine Möglichkeit! Es gibt noch viele andere. Zb kann man ja 
auch anstelle des untersten Bit das oberste nehmen und da dann 
sukzessive alle Bits von x durch linksschieben an dieser Position 
'vorbeischleusen'.
Oder man schiebt sukzessive eine 1 in einem Maskenbyte von einer Stelle 
zur nächsten durch, mit der man dann mittels eines & sich das 
interessierende Bit aus dem x herausholt, oder ....

von fragendermensch (Gast)


Lesenswert?

Danke an euch beide für die schnellen Antworten!

Karl Heinz Buchegger schrieb:
> for( i = 0; i < 32; i++ )
>    {
>      mach was mit ( x & 0x01 );
>      x >>= 1;
>    }

Nach rechts schieben bedeutet, die Zahl wie auf einem Karussell um eine 
Stelle zu drehen, dass das LSB plötzlich das MSB ist und dass das 
zweitkleinste Bit der Zahl nun das LSB ist, nehme ich an.

Ok, dann werde ich das morgen mal im Programm ausprobieren.

Habt noch mal vielen Dank für die Hilfe!!!

von Karl H. (kbuchegg)


Lesenswert?

fragendermensch schrieb:
> Danke an euch beide für die schnellen Antworten!
>
> Karl Heinz Buchegger schrieb:
>> for( i = 0; i < 32; i++ )
>>    {
>>      mach was mit ( x & 0x01 );
>>      x >>= 1;
>>    }
>
> Nach rechts schieben bedeutet, die Zahl wie auf einem Karussell um eine
> Stelle zu drehen, dass das LSB plötzlich das MSB ist und dass das
> zweitkleinste Bit der Zahl nun das LSB ist, nehme ich an.

Nein.
Was rechts rausfällt, ist weg.

Was du meinst trägt den Terminus 'rotieren' und diese Operation gibt es 
zwar auf Assemblerebene aber nicht auf C-Ebene.

von fragendermensch (Gast)


Lesenswert?

fragendermensch schrieb:
>>      x >>= 1;

PS:

x <<= 7;

würde die Zahl x um 7 binäre Stellen nach links verschieben, stimmts?

von Karl H. (kbuchegg)


Lesenswert?

fragendermensch schrieb:
> fragendermensch schrieb:
>>>      x >>= 1;
>
> PS:
>
> x <<= 7;
>
> würde die Zahl x um 7 binäre Stellen nach links verschieben, stimmts?


Alle C-Operationen, die nach dem Muster

   x op= y

aufgebaut sind, sind eine Kurzschreibweise für

   x =   x op (y)

d.h.

    x <<= 2

ist eine Kurzschreibweise für

    x =  x << 2

und x << 2 wiederrum, ist die C-Schreibweise für

   nimm x
   schiebe das Bitmuster nach links (weil die 'Haken' nach links zeigen)
   und zwar um 2 Stellen

d.h. die Operation <<= macht genau diese Operation und weist dann das 
Ergebnis davon wieder der Variablen links vom = zu. Letzters in völliger 
Analogie zu beispielweise

    a *= 5

welches eine Kurzschreibweise für

    a = a * 5

ist, deren Operation selbsterklärend sein sollte und nach genau 
demselben Muster abläuft. Nur eben mit einer Multiplikation * anstelle 
einer Schiebeoperation << (links schieben) bzw. >> (rechts schieben).

D.h. ja

   x <<= 7

verschiebt im Endeffekt das Bitmuster von x um 7 Stellen nach links, 
wobei von rechts mit 0-Bits aufgefüllt wird.

von fragendermensch (Gast)


Lesenswert?

PPS:

dann kann man wahrscheinlich auch folgendes machen!??:

y = ( x <<= 3 );

von fragendermensch (Gast)


Lesenswert?

Danke für die superausführliche Erklärung!

Meine letzte Frage hatte sich mit deinem letzten Posting überschnitten, 
jetzt ist es klar!

Noch mal Danke!!!

von Karl H. (kbuchegg)


Lesenswert?

fragendermensch schrieb:
> PPS:
>
> dann kann man wahrscheinlich auch folgendes machen!??:
>
> y = ( x <<= 3 );


:-)
Natürlich.
In C ist auch eine Zuweisung ein 'arithmetischer Ausdruck', der ein 
Ergebnis hat. Wenn

   a = b = 5;

genau aus diesem Grund zulässig ist, dann ist es selbstverständlich auch

   y = x <<= 3;

Du scheinst irgendwie der Vorstellung verhaftet zu sein, dass << bzw. >> 
irgendwie aussergeöhnlich wären. Das sind sie nicht. Das sind ganz 
normale 'arithmetische Operationen, wie zb + - * / % es auch sind. Die 
Operationen verknüpfen 2 Operanden anhand einer Vorschrift und liefern 
ein Ergebnis.
Bei '*' ist eben die Verknüpfungsvorschrift für a*b dergestalt, dass das 
Ergebnis die Multiplikation von a mit b darstellt.
Und bei a << b ist die Verknüpfungsvorschrift der Operanden dergestalt, 
dass das Ergebnis aus a um b Stellen binär nach links verschoben 
entsteht.

Auch == ist nichts anderes als eine derartige Operation. Das 
Verknüpfungsergebnis von a == b ist eine 1, wenn der Vergleich zutrifft 
und ist 0, wenn er nicht zutrifft. Aber abgesehen davon, dass du so 
etwas wahrscheinlich noch nie als 'arithmetische Operation im Sinne von 
+ - * /' angesehen haben dürftest, gibt es in C da keinen prinzipiellen 
Unterschied! Es ist ein Operator (genauso wie != < > <= >=) der einen 
linken Operanden mit einem rechten Operanden anhand einer vorschrift 
miteinander verknüpft und anhand dieser Vorschrift ein Ergebnis erzeugt. 
Was auch immer dann mit diesem Ergebnis weiter passiert liegt ausserhalb 
der Kontrolle des Operators und ist auch nicht sein Bier.

D.h. es ist möglich zu schreiben

   d = ( c = ( a == b ) * 5 ) << 3;

und dieser Ausdruck hat einen exakt definierten Ablauf.

von fragendermensch (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> d = ( c = ( a == b ) * 5 ) << 3;
>
> und dieser Ausdruck hat einen exakt definierten Ablauf.

Das ist mir grade ein bisschen hoch, aber Danke für die Erklärung!
C ist wirklich enorm vielseitig, wie mir scheint.


Habe noch eine andere Frage zu Variablen:

Habe hier ein Programm-Listing für Arduino, wie es scheint.

Dort werden folgende Variablen deklariert:

unsigned char x;
double y;
long int z;

Können diese Variabeltypen beim Programmieren mit dem AVR-Studio so 
einfach übernommen werden oder muss man etwas besonderes beachten?

"unsigned char x" kann man sicher einfach als "uint8_t x" ausdrücken, 
aber bei den anderen bin ich mir nicht sicher, zumal verschiedene 
Quellen unterschiedliche Angaben dazu machen, was "double" und "long 
int" sind.

von Karl H. (kbuchegg)


Lesenswert?

fragendermensch schrieb:
> Karl Heinz Buchegger schrieb:
>> d = ( c = ( a == b ) * 5 ) << 3;
>>
>> und dieser Ausdruck hat einen exakt definierten Ablauf.
>
> Das ist mir grade ein bisschen hoch,

:-)
macht nix.

Wer sowas schreibt, sollte sowieso mit dem nassen Fetzen verjagt werden.


> Habe noch eine andere Frage zu Variablen:
>
> Habe hier ein Programm-Listing für Arduino, wie es scheint.
>
> Dort werden folgende Variablen deklariert:
>
> unsigned char x;
> double y;
> long int z;
>
> Können diese Variabeltypen beim Programmieren mit dem AVR-Studio so
> einfach übernommen werden oder muss man etwas besonderes beachten?

sind alles C STandard Datentypen, die jeder C Compiler kennen muss.

>
> "unsigned char x" kann man sicher einfach als "uint8_t x" ausdrücken,

ja.

> aber bei den anderen bin ich mir nicht sicher, zumal verschiedene
> Quellen unterschiedliche Angaben dazu machen, was "double" und "long
> int" sind.

long ist auf einem AVR ein int32_t   (also ein 32 Bit Integer mit 
Vorzeichen)

double ist beim gcc auf einem AVR dasselbe wie ein float: Ein Floating 
Point Wert mit 4 Bytes.


Mach auf einem AVR lieber einen Bogen um die Standard-Datentypen. Die 
AVRs sind zu klein, als das du dir da Überraschungen einhandeln willst. 
Lieber explizit sein

  uint8_t               unsigned char
  int8_t                signed char
  char                  char
  int16_t               int
  uint16_t              unsigned int
  int32_t               long
  uint32_t              unsigned long

  float                 float
  double                float

Einen 'echten' double gibt es beim gcc nicht. double und float sind 
beides float.


gib im Speziellen darauf acht, ob der Datentyp int SINNVOLL benutzt 
wird. Oft ist es so, dass ein int benutzt wird, wo es ein uint8_t auch 
tut, weil die Zahlen niemals so groß werden, dass 8 Bit nicht reichen. 
8-Bit Arithmetik ist aber für den AVR einfacher als 16 Bit Arithmetik!

von fragendermensch (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Mach auf einem AVR lieber einen Bogen um die Standard-Datentypen. Die
> AVRs sind zu klein, als das du dir da Überraschungen einhandeln willst.
> Lieber explizit sein
>
>   uint8_t               unsigned char
>   int8_t                signed char
>   char                  char
>   int16_t               int
>   uint16_t              unsigned int
>   int32_t               long
>   uint32_t              unsigned long
>
>   float                 float
>   double                float
>
> Einen 'echten' double gibt es beim gcc nicht. double und float sind
> beides float.

Danke für die Infos, habe es direkt notiert.

Werde es gleich ausprobieren. Der betreffende Programmteil funktioniert 
zwar bei höheren Zahlen anscheinend richtig, beim Input kleinerer Zahlen 
wird aber plötzlich alles Null im Output, daher vermute ich ein Problem 
bei den Variabeltypen...

von fragendermensch (Gast)


Lesenswert?

So, mit den neuen Variabel-Typen funktioniert es jetzt! Noch mal 
Danke!!!


Gibt es eigentlich einen Befehl, der die Bits einer Variablen spiegeln 
kann (also so, dass das LSB zum MSB wird und umgekehrt)?

von Basti M. (counterfeiter)


Lesenswert?

Ja, nennt sich subtrahieren...

von Hawa M. (hawamand)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Aber eigentlich will man auf einem AVR die Operation '1 << i' nicht
> machen, weil dem AVR die dazu notwendige Hardware-Ausstattung fehlt.
Weil dem AVR der Befehl shift-left fehlt?

von Marius W. (mw1987)


Lesenswert?

Nein, der Shift-Left-Befehl ist vorhanden. Aber der AVR hat keinen 
Barrelshifter, sodass Shifts um mehr als eine Stelle ziemlich unhandlich 
werden.

Gruß
Marius

von flo (Gast)


Lesenswert?

fragendermensch schrieb:
> Gibt es eigentlich einen Befehl, der die Bits einer Variablen spiegeln
> kann (also so, dass das LSB zum MSB wird und umgekehrt)?

könnte man für sowas und ähnliche allgemeine probleme (z.b. variable in 
string umwandeln) eine Hilfsdatei schreiben und im AVR-Studio einbinden?

dann hätten alle was davon

von Karl H. (kbuchegg)


Lesenswert?

flo schrieb:
> fragendermensch schrieb:
>> Gibt es eigentlich einen Befehl, der die Bits einer Variablen spiegeln
>> kann (also so, dass das LSB zum MSB wird und umgekehrt)?
>
> könnte man für sowas und ähnliche allgemeine probleme (z.b. variable in
> string umwandeln) eine Hilfsdatei schreiben und im AVR-Studio einbinden?
>
> dann hätten alle was davon

Ähm.
Es hätten auch alle was davon, wenn die Neulinge sich ein C-Buch kaufen 
würden, es durcharbeiten und dann zumindest einen groben Überblick haben 
würden, welche Operatoren es gibt und welche vorgefertigten Funktionen 
sie bereits zur Verfügung haben.

> (z.b. variable in string umwandeln)
Was stört dich an
  itoa
  utoa
  ltoa
  ultoa
  ftoa
  sprintf

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.