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
Hallo Danke schonmal! Aber: Einfacher gehts nicht? Hmmm...dürfte wohl bei sehr grosszügigem Einsatz einiges an Rechenleistung kosten... Gruss Michael
"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
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!
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.
"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
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.
> obhwohl ich auch mit letzterem kein Problem habe, es zu lesen...
...und zu merken, dass da ne Klammer fehlt...;-)
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. ;-)
> 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.
@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
> 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";
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
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.
...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!
...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...
> 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:
@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
> 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.
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ß
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).
@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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.