mikrocontroller.net

Forum: Compiler & IDEs Unterschied zweier Variablen


Autor: mr.chip (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Ich habe 2 uint8_t und möchte den Unterschied der beiden Variabeln
berechnen. In Java mache ich das mittels Math.abs(a - b), doch wie
mache ich sowas in C elegant?

Gruss

Michael

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
c = a > b ? a - b : b - a;


Peter

Autor: mr.chip (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Danke schonmal! Aber: Einfacher gehts nicht? Hmmm...dürfte wohl bei
sehr grosszügigem Einsatz einiges an Rechenleistung kosten...

Gruss

Michael

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Hmmm...dürfte wohl bei sehr grosszügigem Einsatz einiges an
Rechenleistung kosten..."


Nö, überhaupt nicht.

Is ja nur ein Test und ne Subtraktion.
Aufm ATMega88 bei 20MHz unter WINAVR dauerts max 0,3µs:
c = a > b ? a - b : b - a;
  5c:   68 17           cp      r22, r24
  5e:   18 f4           brcc    .+6             ; 0x66 <test+0xa>
  60:   86 1b           sub     r24, r22
  62:   68 2f           mov     r22, r24
  64:   01 c0           rjmp    .+2             ; 0x68 <test+0xc>
  66:   68 1b           sub     r22, r24

Ist bestimmt 100 mal schneller als in Java.


Peter

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wieder einer der glaubt, dass etwas, das sich kurz schreibt auch nicht
lange dauern kann und sich dann wundert, dass ein klitzekleines printf
oder so (am besten noch in einer ISR) das ganze System abschießt...

Aber im Ernst: Ich kenne mich mit Java nicht sonderlich aus, denke
aber, dass hinter der Schreibweise 'Math.abs(a - b)' im Prinzip nix
anderes (zumindest nicht viel anderes) steckt als das, was Peter
gepostet hat. Und kürzer gehts nun wirklich nicht!

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde einfach die Funktion abs benutzen, so wie du das in Java auch
getan hast. Die ist zwar vermutlich auch nicht schneller, aber
zumindest besser lesbar.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Die ist zwar vermutlich auch nicht schneller,..."

Stimmt, dauert sogar 0,45µs (9 Zyklen).


"... aber zumindest besser lesbar."

Ist mir auch schon aufgefallen, daß manche Leute Schwierigkeiten mit
dem ?: haben. Warum nur ?


Peter

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe damit keine Schwierigkeiten, aber abs() drückt eben einfach
besser die Absicht aus, ähcnlich wie

    loop_until_bit_is_clear(PORTB, PORTB5);

die Absicht besser ausdrückt, als:

    while (PORTB & (1 << PORTB5);

obhwohl ich auch mit letzterem kein Problem habe, es zu lesen.

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> obhwohl ich auch mit letzterem kein Problem habe, es zu lesen...

...und zu merken, dass da ne Klammer fehlt...;-)

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Natürlich hab ich das gleich nach dem Abschicken gemerkt, aber ich
dachte mir, ich brauch's nicht extra erwähenen, weil das bestimmt
schon ein anderer innerhalb von weniger als 5 Mintuen tun wird. ;-)

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Die ist zwar vermutlich auch nicht schneller, aber
> zumindest besser lesbar.

Zur Not kann man dann auch immer noch den Präprozessor
auspacken:

#define ABS( a )  (a) < 0 ? (a) : -(a)


  c = ABS( a - b );

Die üblichen Hinweise zu den Makro-Fallen spar ich mir
jetzt.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Karl Heinz

"#define ABS( a )  (a) < 0 ? (a) : -(a)"


Und schon bist Du in die Falle getappt !

a und b sind vom Typ uint8_t, also unsigned und damit ist der Ausdruck
(a) < 0 immer falsch.

Das AVR-GCC abs-Macro erweitert deshalb auf signed int, macht dann erst
die Subtraktion, dann den Betrag und schmeißt das High-Byte wieder weg.
Deshalb dauert es auch länger.
Ob das der AVR-GCC nur zufällig so macht wie gewünscht, weiß ich
nicht.
abs() auf unsigned klingt jedenfalls irgendwie sinnlos.

Daher finde ich a > b ? für unsigned Werte besser lesbar als abs(). Und
ich habe die Garantie, daß er genau das macht, was ich will.


Peter

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Und schon bist Du in die Falle getappt !

Grrr.
Ich gelobe Besserung:

  for( int i = 0; i < 100; ++i )
    std::cout << "Ich werde mir in Zukunft die "
                 "Datentypen besser ansehen\n";

Autor: mr.chip (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Könnte das mit dem unsigned mein Problem sein?

uint8_t d = (ADCH > gbc_ee_buffer) ? (ADCH - gbc_ee_buffer) : 29;

Dieser Ausdruck ist immer falsch, d.h. ich kriege immer 29... (Obwohl
es mehr als nur schlüssige Hinweise gibt, dass sowohl ADCH wie auch
gbc_ee_buffer mal grösser sind.)

Was ich meinte wegen langsam: Ich habe auf ne Lösung gehofft à la
'subtrahieren' und dann irgend wo vielleicht noch ein Bit drehen oder
so... Hoffentlich hält ihr mich jetzt nicht für total naiv ;-) - ich
sollte mich aber echt mal mit der grundlegenden Art und Weise, wie
Zahlen gespeichert und verarbeitet werden, befassen! ^^

Gruss

Michael

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schreib statt der 29 mal ne 42 rein, vielleicht klappts dann besser;-)

Im Ernst: Man kann es sso versuchen, wie man es in Assembler machen
kann: Einfach ins blaue hinein subtrahieren. Wenn was negatives
rauskommt, gibts nen Underrun in der Zielvariable. Dann das
Zweierkomplement aufdröseln (MSB checken, wenn gesetzt, dann
Bitkomplement bilden und eins dazuaddieren) und --Zack-- hat man den
Betrag.

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...ach ja, Ergänzung: Das ganze funktioniert natürlich nur dann, wenn
die Werte der Variablen (als uint8_t) im Bereich -128...+127 liegen.
Darüberhinaus gehts mit 8 Bit net!

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...Noch ne Ergänzung/Korrektur: Ich meinte natürlich nicht die Werte der
Variablen an sich, sondern die Differenz zwischen den beiden. Sorry,
noch nicht ganz wach...

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich habe auf ne Lösung gehofft à la 'subtrahieren' und dann irgend
> wo vielleicht noch ein Bit drehen oder so...

Sowas in der Art geht schon, aber wird dann schwierig in C. Wenn's dir
auf Geschwindigkeit und Codegröße ankommt, wird's wohl am besten
Assembler sein. Etwa sowas wie das müßte gehen:

    sub r16, r17
    brsh next
    neg r16
next:

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Rolf,

siehe meine Antwort an Karl Heinz.

Das klappt nur für signed Zahlen bis max +127 richtig.

In C würde es so aussehen:
a -= b;
if( a & 0x80 )
  a = -a;


Peter

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Das klappt nur für signed Zahlen bis max +127 richtig.

Dann nenne mir mal ein paar vorzeichenlose 8-bit-Zahlen, mit denen mein
Code nicht funktioniert.

Autor: ich (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peters Code geht immer dann schief, wenn die Differenz der beiden
8bit-Zahlen größer als 127 ist.
Aber setzt der Compiler diesen C-Code wirklich in Rolfs Assemblercode
um?
Rolf testet das Carry-Flag, Peter das Vorzeichenbit?
Gruß

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Versuch's mal so:
unsigned char c = a - b;
if (a < b)
    c = -c;

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Um noch den Unterschied zu erläutern: Peter geht davon aus, daß nach der
Subtraktion a (wenn man es als vorzeichenbehaftet interpretiert) negativ
ist, wenn es vorher kleiner als b war. Diese Annahme ist aber nur dann
in jedem Fall korrekt, wenn a und b auch tatsächlich
vorzeichenbehaftete Zahlen sind. Für vorzeichenlose Zahlen muß der
Vergleich anders aussehen. Deshalb enthält mein Asssembler-Code auch
ein brsh (vorzeichenloser Vergleich) und kein brge
(vorzeichenbehafteter Vergleich).

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Rolf,

hast recht.

Mich hat nur das BRSH verwirrt, da muß ich immer nachsehen, welches
Flag damit gemeint ist.
Ich schreib lieber BRCC, dann weiß ich, daß es das Carry ist.

Das Carry ist praktisch das 8. Ergebnisbit, aber leider unter C nicht
verfügbar.


Peter

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe mir hier über die Flags ehrlich gesagt überhaupt keine Gedanken
gemacht, sondern einfach den Befehl genommen, der beim Vergleich zweier
vorzeichenloser Zahlen springt, wenn die erste größer oder gleich der
zweiten ist.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.