Forum: Compiler & IDEs Unterschied zweier Variablen


von mr.chip (Gast)


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

von Peter D. (peda)


Lesenswert?

1
c = a > b ? a - b : b - a;


Peter

von mr.chip (Gast)


Lesenswert?

Hallo

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

Gruss

Michael

von Peter D. (peda)


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:
1
c = a > b ? a - b : b - a;
2
  5c:   68 17           cp      r22, r24
3
  5e:   18 f4           brcc    .+6             ; 0x66 <test+0xa>
4
  60:   86 1b           sub     r24, r22
5
  62:   68 2f           mov     r22, r24
6
  64:   01 c0           rjmp    .+2             ; 0x68 <test+0xc>
7
  66:   68 1b           sub     r22, r24

Ist bestimmt 100 mal schneller als in Java.


Peter

von johnny.m (Gast)


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!

von Rolf Magnus (Gast)


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.

von Peter D. (peda)


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

von Rolf Magnus (Gast)


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.

von johnny.m (Gast)


Lesenswert?

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

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

von Rolf Magnus (Gast)


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. ;-)

von Karl heinz B. (kbucheg)


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.

von Peter D. (peda)


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

von Karl heinz B. (kbucheg)


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";

von mr.chip (Gast)


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

von johnny.m (Gast)


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.

von johnny.m (Gast)


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!

von johnny.m (Gast)


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...

von Rolf Magnus (Gast)


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:

von Peter D. (peda)


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:
1
a -= b;
2
if( a & 0x80 )
3
  a = -a;


Peter

von Rolf Magnus (Gast)


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.

von ich (Gast)


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ß

von Rolf Magnus (Gast)


Lesenswert?

Versuch's mal so:
1
unsigned char c = a - b;
2
if (a < b)
3
    c = -c;

von Rolf Magnus (Gast)


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).

von Peter D. (peda)


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

von Rolf Magnus (Gast)


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.

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.