Forum: Mikrocontroller und Digitale Elektronik Frage Bitoperation


von Noob (Gast)


Lesenswert?

Hallo liebe Leute,

habe eine Frage, wo ich nicht so recht weiterkomme. Es sind zwei uint8_t 
Variablen vorhanden, nennen wir var_new und var_old. Nun will ich ein 
bestimmtes Bit (sagen wir Bit 0) von var_new nach var_old kopieren.

Aktueller Versuch sieht so aus:
1
var_old |= var_new&(1<<0);

Nur ist die Operation allerdings falsch, da es schlichtweg nicht 
funktioniert. Ich behelfe mir aktuell mit einer einfache if-Abfrage
1
if (var_new&(1<<0))
2
   { var_old |= (1<<0);}
3
else 
4
   { var_old &= ~(1<<0);}

Aber das muss doch irgendwie in einem Schritt gehen! Wahrscheinlich sehe 
ich den Wald vor lauter Bäume nicht...

von Daniel A. (daniel-a)


Lesenswert?

1
var_old = (var_new & (1<<0)) | (var_old & ~(1<<0));

von asdfasd (Gast)


Lesenswert?

1
var_old ^= (var_old ^ var_new) & (1<<0);

von c-hater (Gast)


Lesenswert?

Noob schrieb:

> Aber das muss doch irgendwie in einem Schritt gehen!

Nein. Auch wenn du einen komplexen Ausdruck in C in einer Zeile 
schreiben kannst, werden es dadurch nicht weniger "Schritte". Die 
if-Konstruktion ist schon garnicht schlecht, der Compiler kann die recht 
gut optimieren.

Wenn du die unbedingt in eine Zeile quetschen willst, wazu auch immer 
das gut sein soll, kannst du sie ja auch so schreiben:

(var_new&(1<<0))? var_old |= (1<<0): var_old &= ~(1<<0);

Auf'm AVR8 kommt dann vermutlich sowas raus:

sbrc reg_new,0
ori reg_old,1
sbrs reg_new,0
andi reg_old,~1

was mit vier Takten schon nicht schlecht ist. Vielleicht schafft der 
Compiler aber sogar das Optimimum

bst reg_new,0
bld reg_old,0

Aber auch das sind immer noch ZWEI Schritte, also zwei Takte. In einem 
Schritt ist das Problem schlicht nicht lösbar. Jedenfalls nicht auf 
einem AVR8.

Bei den anderen Vorschlägen im Thread wird mit einiger 
Wahrscheinlichkeit etwas herauskommen, was weniger effizienten Code 
erzeugt. Irgendwas in der Art:

andi reg_old,~1
andi reg_new,1
or reg_old,reg_new

Merke: Was du in C hinschreibst, interessiert den Gasmann. Wichtig ist 
letztlich, was der Compiler draus macht. Auf C-Ebene sollte es vor allem 
eins sein: gut lesbar. Und dafür ist es NICHT hilfreich, soviel wie 
möglich in eine Zeile zu quetschen. Und davon wird der erzeugte Code 
typischerweise auch weder schneller noch kleiner. Also: kompletter 
Unsinn, hier zu "optimieren".

von Leo C. (rapid)


Angehängte Dateien:

Lesenswert?

Auf AVR (avr-gcc 5.4.0) liefert die Variante
Daniel A. schrieb:
> var_old = (var_new & (1<<0)) | (var_old & ~(1<<0));

diesen Assembler-Code:
1
bc_andor:
2
  lds r25,var_new
3
  lds r24,var_old
4
  bst r25,0
5
  bld r24,0
6
  sts var_old,r24
7
  ret

Andere Varianten schneiden schlechter ab.

von Wilhelm M. (wimalopaan)


Lesenswert?

Diese Optimierung zu bst/bld wird aber auch nur für Bit 0 gemacht ...

zumindest bei avr-g++ 7.2.0 und 8.0.0

von Noch einer (Gast)


Lesenswert?

> Aber das muss doch irgendwie in einem Schritt gehen!

Da haben wohl die Konstrukteure des ENIAC eine falsche Entscheidung 
getroffen. Und danach hat 80 Jahre lang kein Prozessorentwickler mehr 
darüber nachgedacht.

von Jim M. (turboj)


Lesenswert?

Noch einer schrieb:
> Und danach hat 80 Jahre lang kein Prozessorentwickler mehr
> darüber nachgedacht.

Falsch. Intel hat in den 80gern beim 8051 µC Bit-addressierbaren 
Speicher vorgesehen.

Bei Cortex-M3 könnte man was ähnlich über Bitband Addressen tricksen, 
aber da findet dann ein read-modify-write Zyklus innerhalb des 
Prozessors statt.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

c-hater schrieb:
> Wenn du die unbedingt in eine Zeile quetschen willst, wazu auch immer
> das gut sein soll, kannst du sie ja auch so schreiben:
Da fehlen noch Klammern:
http://codepad.org/t3NSZth8

Noob schrieb:
> Aktueller Versuch sieht so aus:
> var_old |= var_new&(1<<0);
> Nur ist die Operation allerdings falsch, da es schlichtweg nicht
> funktioniert.
Oh doch, aber funktioniert nur zur Hälfte.
Also nur, wenn ein Bit zu setzen ist. Denn nur das kann das '|' 
bewirken.

Du müsstest also noch eine zweite Zeile hinzufügen, die das Bit vorher 
löscht:
1
    var_old &= ~(1<<0);         // erst löschen
2
    var_old |= var_new&(1<<0);  // ggfs wieder setzen
http://codepad.org/Y8hbvebb

von Wilhelm M. (wimalopaan)


Angehängte Dateien:

Lesenswert?

Anbei ein Testprogramm, um es mit avr-g++ und allen Bits 0...7 und 
Algorithmen zu testen. Es fällt auf, dass die beste Optimierung nur bei 
Bit 0 gemacht wird.

Mir ist nicht klar warum?

von Noob (Gast)


Lesenswert?

Wow, danke euch für die echt lehrreiche Gesprächsrunde :)

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.