Hi, ich habe zwei Variablen, nicht vorzeichenbehaftet: uint8_t a=0, b=0; //können werte von 0-255 enthalten. Ich möchte b von a abziehen und auf das Ergebnis "reagieren" if ((a-b)>=0) { dies } else { das } Wenn a kleiner b ist, dann wäre das Ergebnis ja negativ und damit kleiner Null. Wird das hierbei berücksichtigt oder muss ich noch irgenwo "casten"? Wenn das so nicht geht, würde das dann gehen? if ((int16_t(a-b))>=0) Oder müsste ich es so machen? if ( ( (int16_t a)-(int16_t b) ) >=0 ) Danke Gruß TS
Thorsten S. schrieb: > Wenn a kleiner b ist, dann wäre das Ergebnis ja negativ und damit > kleiner Null. Wenn du unsigned Datentypen verwendest, gibt es keine negativen Ergebnisse. 2 - 3 = 255
Thorsten S. schrieb: > Oder müsste ich es so machen? > > if ( ( (int16_t a)-(int16_t b) ) >=0 ) fast.... if((((int16_t)a)-((int16_t)b))>=0)
Die Berechnung wird natürlich ohne Probleme durchgeführt. Wenn das Ergebnis negativ sein würde, wird das wieder auf Positive Zahlen abgebildet. Bei 8-Bit Variablen bist du dann Mathematisch ausgedrückt in einem "Endlicher Körper" von 2^8=256. Es gibt nur Zahlen zwischen 0 und 255. Das Ergebnis wird automatisch Modulo 256 gerechnet. Beispiel: 11 - 42 = -31 => -31 Mod 256 = 225
Einfach nicht voneinander abziehen oder erst nach dem Vergleich abziehen:
1 | if(a >= b) { |
2 | // Differenz garantiert >= 0
|
3 | } else { |
4 | // Differenz negativ
|
5 | }
|
Wolfgang schrieb: > Wenn du unsigned Datentypen verwendest, gibt es keine negativen > Ergebnisse. > 2 - 3 = 255 Ich dachte das wäre aus meinen Ansätzen mit dem Type Cast zu einem sigend Typ klar. EAF schrieb: > if((((int16_t)a)-((int16_t)b))>=0) Danke. Hannes J. schrieb: > Einfach nicht voneinander abziehen oder erst nach dem Vergleich > abziehen: Genial, manchmal hat man einen Knoten im Hirn... Gruß TS
Thorsten S. schrieb: > Wolfgang schrieb: >> Wenn du unsigned Datentypen verwendest, gibt es keine negativen >> Ergebnisse. >> 2 - 3 = 255 > > Ich dachte das wäre aus meinen Ansätzen mit dem Type Cast zu einem > sigend Typ klar. Nur weil du mit (int16_t(a-b)) aus den bspw. 255 ein signed machst, wird das im Nachhinein nicht negativ. Warum rechnest du die Differenz aus, wenn du wissen willst, ob a>=b ist?
Thorsten S. schrieb: > uint8_t a=0, b=0; //können werte von 0-255 enthalten. > if ((a-b)>=0) > Wenn a kleiner b ist, dann wäre das Ergebnis ja negativ und damit > kleiner Null. Wird das hierbei berücksichtigt oder muss ich noch irgenwo > "casten"? Was heißt "berücksichtigt"? Auf Grund der Integer Promotion werden a und b erst mal nach (signed) int konvertiert, bevor die Rechnung ausgeführt wird. Das passiert in C grundsätzlich mit allem, was kleiner ist als int. Das Ergebnis kann also sehr wohl negativ sein.
:
Bearbeitet durch User
In diesem Fall muss man gar nichts machen, weil die Operanden sowieso nach "signed integer" promoted werden.
:
Bearbeitet durch User
Rolf M. schrieb: > Auf Grund der Integer Promotion Bullshit. Wenn sowohl a als auch b vom Typ uint8_t sind [1] dann wird für die Rechnung (a-b) gar nichts promoted. Für den Vergleich mit dem int Literal 0 würde ein Compiler vielleicht promoten. Aber da liegt das Kind ja bereits im Brunnen. Und ein vernünftiger Compiler (jeder?) würde erkennen, daß ein uint8_t (bzw jeder unsigned) immer >= 0 ist. Er würde also den ganzen Vergleich wegoptimieren und eine Warnung ausspucken. [1] wohl unsigned char auf allen nicht-exotischen Platformen
:
Bearbeitet durch User
Axel S. schrieb: > Bullshit. Wenn sowohl a als auch b vom Typ uint8_t sind [1] dann > wird für die Rechnung (a-b) gar nichts promoted. Lern die Sprache, bevor du so einen Unsinn von dir gibst. Die Promotion findet statt!
Bei uint8_t a=0, b=0; entspricht die Rechnung (a - b) aufgrund der Promotion Regeln dem Ausdruck ((int)a - (int)b) weil, simpel ausgedrückt, alles kleiner als int als int gerechnet wird und int definitiv grösser als uint8_t ist. Anders wärs bei unsigned a=0, b=0; weil a und b dabei nicht kleiner als int sind.
Axel S. schrieb: > Wenn sowohl a als auch b vom Typ uint8_t sind [1] dann wird für > die Rechnung (a-b) gar nichts promoted. Das ist kompletter … > Bullshit. > Und ein vernünftiger Compiler (jeder?) würde erkennen, daß ein uint8_t > (bzw jeder unsigned) immer >= 0 ist. Er würde also den ganzen Vergleich > wegoptimieren und eine Warnung ausspucken. Auch das ist … > Bullshit.
Axel S. schrieb: > Bullshit. Wenn sowohl a als auch b vom Typ uint8_t sind [1] dann > wird für die Rechnung (a-b) gar nichts promoted. Für den Vergleich mit > dem int Literal 0 würde ein Compiler vielleicht promoten. Aber da > liegt das Kind ja bereits im Brunnen. > > Und ein vernünftiger Compiler (jeder?) würde erkennen, daß ein uint8_t > (bzw jeder unsigned) immer >= 0 ist. Er würde also den ganzen Vergleich > wegoptimieren und eine Warnung ausspucken. Du irrst Avr gnu++c17 -Os
1 | byte a = 1; |
2 | byte b = 44; |
3 | |
4 | if(a-b < 0) cout << F("a-b")<< endl; |
5 | if(b-a < 0) cout << F("b-a")<< endl; |
Ausgabe:
1 | a-b |
Um das zu verstehen muss man wissen, dass der C-Compiler unterschiedliche Algorithmen (Rechen-Methoden) für unterschiedliche Datentypen anwendet. Wenn die Operanden alle "unsinged int" sind, verwendet er einen Algorithmus der nur damit funktioniert und auch nur ein solches Ergebnis liefern kann. Subtraktionen können falsche Ergebnisse liefern, wenn der Wertebereich des Ergebnisses überschritten wird. In diesem Fall eben bei negativen Zahlen. Bei Additionen und Multiplikationen können Überläufe stattfinden. "unsigned int" ist normalerweise 16 Bit. Wenn du 20000·40 berechnest, hast du so einen Überlauf. Es sei denn, du vastest wenigstens einen Operand auf "long int". Dann wird nämlich der größere Algorithmus für 32 Bit verwendet. Bei Divisionen on Integer Zahlen werden keine Nachkomma-Stellen berechnet, weil der Algorithmus das nicht kann. Ist hingegen einer der Operanden eine Fließkommazahl, dann wird der komplexere Algorithmus verwendet, der Nachkomma-Stellen im Ergebnis liefert. Zusätzlich muss natürlich das Ergebnis in eine dazu passende Variable geschrieben werden, sonst wird es nochmal umgewandelt, was eventuell mit Verlusten behaftet ist. > int8_t ergebnis = 3.0 * 5.0; Das ist OK, weil das Ergebnis 15 ist und in die Variable passt. > int8_t ergebnis = -3.0 * 5.0; Das ist nicht OK, weil die Variable keine Negativen Zahlen speichern kann. Das Resultat wird fehlerhaft sein. Inden meisten Programmiersprachen (auch C, allerdings nicht auf AVR) ist die Division durch 0 die einzige ungültige Operation, die zu einem Programmabbruch führt. Alle anderen ungültigen Kombinationen führen lediglich zu kaputten Ergebnissen, das Programm läuft aber trotzdem weiter. Wahrscheinlich gibt es noch mehr Ausnahmen, aber so habe ich es mir gemerkt.
EAF schrieb: > Avr gnu++c17 -Os Mal davon abgesehen, dass du C++ benutzt, was genau ist dein "byte"? Es ist kein std::byte.
EAF schrieb: > byte a = 1; > byte b = 44; > if(a-b < 0) cout << F("a-b")<< endl; > if(b-a < 0) cout << F("b-a")<< endl; > > Ausgabe:a-b Das überrascht mich. Wie ist das möglich?
Stefan ⛄ F. schrieb: > … Trink erst mal einen Kaffee, boote anständig durch, und dann schreibste das nochmal. Ohne all den Quatsch. Oliver
Stefan ⛄ F. schrieb: > "unsigned int" ist normalerweise 16 Bit Das hat noch nicht einmal in jeder Zeit gestimmt, um die es in der Retro-Diskussion im Nachbarthread geht. Stimmen tut nur: es ist mindestens 16 Bit breit. Und so selten sind 32-Bitter auch in Mikrocontrollern nicht.
:
Bearbeitet durch User
mh schrieb: > was genau ist dein "byte"?
1 | if(is_same<byte,unsigned char>())cout << F(" byte ist unsigned char") << endl; |
Ausgabe:
1 | byte ist unsigned char |
Oliver S. schrieb: > Trink erst mal einen Kaffee, boote anständig durch, und dann schreibste > das nochmal. Ohne all den Quatsch. Yep. Habe ich gestern eine Feier verpasst? Eine mit genug Alkohol, um die Birnen noch jetzt erheblich zu beeinflussen. Ich bin auch ein wenig über etliche der Antworten verwirrt. Ist immerhin eine Standardfrage, auf die es eigentlich eine Standardantwort gibt. Statt dessen kommt erst einmal ein Bündel Quatsch.
Stefan ⛄ F. schrieb: > Das überrascht mich. Wie ist das möglich?
1 | byte a = 1; |
2 | byte b = 44; |
3 | auto c = a-b; |
4 | if(is_same<decltype(c),int>())cout << F("ist int") << endl; |
Ausgabe: ist int
Stefan ⛄ F. schrieb: >> int8_t ergebnis = -3.0 * 5.0; > Das ist nicht OK, weil die Variable keine Negativen Zahlen speichern > kann. Das Resultat wird fehlerhaft sein. Oh jeh...
Guten Morgen. Mal am praktischen Beispiel getestet:
1 | #include <stdint.h> |
2 | #include <stdio.h> |
3 | // gcc -Wall -Wextra -pedantic -std=c11 -Os -o "x" "x.c"
|
4 | uint8_t f1(uint8_t a, uint8_t b) { return a - b; } |
5 | int f2(uint8_t a, uint8_t b) { return a - b; } |
6 | int main(void) { |
7 | uint8_t a = 1; |
8 | uint8_t b = 2; |
9 | printf("%d %u\n", a-b, a-b); |
10 | printf("%d %u\n", f1(a,b), f1(a,b)); |
11 | printf("%d %u\n", f2(a,b), f2(a,b)); |
12 | }
|
13 | // -1 4294967295
|
14 | // 255 255
|
15 | // -1 4294967295
|
Da finde ich Ausgabezeile Zwei recht interessant.
Norbert schrieb: > Da finde ich Ausgabezeile Zwei recht interessant. Ich nicht. Die implizite Type Konvertierung hat doch voll geklappt. Nach Lehrbuch.
(prx) A. K. schrieb: > Norbert schrieb: >> Da finde ich Ausgabezeile Zwei recht interessant. > > Weshalb? Nun, wenn a und b erst nach int konvertiert würden und dann gerechnet wird, sollte -1 zurück gegeben werden. So aber wird uint8_t gerechnet und dann erst nach int konvertiert.
Thorsten S. schrieb: > Ich möchte b von a abziehen und auf das Ergebnis "reagieren" > > if ((a-b)>=0) Du möchtest also partout mit dem Kopf durch die Wand. Aha, darum wird in den folgenden Beiträgen so unsäglich viel herumgecastet. Warum nicht einfach ein simpler Vergleich 'if (a>=b)...' oder warum unbedingt als Operanden zweimal unsigned char und kein simpler int? Mußt du so elend mit RAM sparen, wo das Ergebnis ohnehin negativ sein kann und auch betragsmäßig größer als ein signed char? W.S.
Norbert schrieb: > So aber wird uint8_t gerechnet und dann erst nach int konvertiert. Falsch! Es wird mit int gerechnet und beim return nach uint8_t konvertiert.
EAF schrieb: > Norbert schrieb: >> So aber wird uint8_t gerechnet und dann erst nach int konvertiert. > > Falsch! > Es wird mit int gerechnet und beim return nach uint8_t konvertiert. … obwohl ein int Wert zurückgegeben wird? Interessant!
Norbert schrieb: > (prx) A. K. schrieb: >> Norbert schrieb: >>> Da finde ich Ausgabezeile Zwei recht interessant. >> >> Weshalb? > Nun, wenn a und b erst nach int konvertiert würden und dann gerechnet > wird, sollte -1 zurück gegeben werden. > So aber wird uint8_t gerechnet und dann erst nach int konvertiert. Nö ... Oder vielleicht doch, dank UB?
Norbert schrieb: > (prx) A. K. schrieb: >> Norbert schrieb: >>> Da finde ich Ausgabezeile Zwei recht interessant. >> >> Weshalb? > Nun, wenn a und b erst nach int konvertiert würden und dann gerechnet > wird, sollte -1 zurück gegeben werden. Wie soll da jemals -1 zurück gegeben werden können? Die Funktion f1 hat als return-Typ uint8_t. Der kann nicht -1 sein. > uint8_t f1(uint8_t a, uint8_t b) { return a - b; } Hier wird a - b in int gerechnet. Das Ergebnis ist vom Typ int und hat für a=1 und b=2 den Wert -1. Allerdings hast du als Return-Typ der Funktion ja uint8_t festgelegt, daher muss der Wert erst in diesen Typ konvertiert werden. > So aber wird uint8_t gerechnet und dann erst nach int konvertiert. Falsche Schlussfolgerung. (prx) A. K. schrieb: > Ich bin auch ein wenig über etliche der Antworten verwirrt. Ist immerhin > eine Standardfrage, auf die es eigentlich eine Standardantwort gibt. Statt > dessen kommt erst einmal ein Bündel Quatsch. Das hab ich mir auch gedacht. Ist die integer promotion und wo sie sich wie auswirkt, wirklich so vielen hier unbekannt?
Norbert schrieb: > Nun, wenn a und b erst nach int konvertiert würden und dann gerechnet > wird, sollte -1 zurück gegeben werden. Beachte: Die zweite Ausgabezeile ist jene mit f1(). Eine Funktion, deren Returnwert als uint8_t deklariert ist.
Norbert schrieb: > obwohl ein int Wert zurückgegeben wird? Interessant! verstehst du dein eigenes Programm nicht? Norbert schrieb: > uint8_t f1(uint8_t a, uint8_t b) { return a - b; } Wo wird da ein int zurückgegeben? Die Antwort: Nirgendwo!
(prx) A. K. schrieb: > Norbert schrieb: >> Nun, wenn a und b erst nach int konvertiert würden und dann gerechnet >> wird, sollte -1 zurück gegeben werden. > > Beachte: Die zweite Ausgabezeile ist jene mit f1(). Eine Funktion, deren > Returnwert als uint8_t deklariert ist. Und der Wert wird dann nach int promoted für den Aufruf von printf, welcher mit einem falschen Conversion Specifier stattfindet...
(prx) A. K. schrieb: > Das hat noch nicht einmal in jeder Zeit gestimmt, um die es in der > Retro-Diskussion im Nachbarthread geht. Offenbar hast du Recht. Ich habe es gerade an PC ausgetestet. Damit muss ich mit dringend im Detail befassen.
mh schrieb: > Oder vielleicht doch, dank UB? Da sehe ich kein UB. Die Rechnung (a - b) ist einwandfrei definiert, und eine Konvertierung in einen Typ ohne Vorzeichen ist es auch.
(prx) A. K. schrieb: > mh schrieb: >> Oder vielleicht doch, dank UB? > > Da sehe ich kein UB. Die Rechnung (a - b) ist einwandfrei definiert, > und eine Konvertierung in einen Typ ohne Vorzeichen ist es auch. Nicht die Rechnung, der printf-Aufruf.
F1() gibt natürlich uint8_t zurück (also Ausgabezeile Zwei) Pardon an alle. Off-by-One Error meinerseits. Sorry
Beitrag #6893419 wurde vom Autor gelöscht.
(prx) A. K. schrieb im Beitrag #6893419: > mh schrieb: >> Und der Wert wird dann nach int promoted für den Aufruf von printf, >> welcher mit einem falschen Conversion Specifier stattfindet... > > Ist ein falscher Conversion Specifier für int<=>unsigned UB? Wenn ich den Standard richtig verstehe (aus ISO/IEC 9899:201x, N1570):
1 | 7.21.6.1 The fprintf function |
2 | [...] |
3 | 8) o,u,x,X The unsigned int argument is converted [...] |
4 | 9) If a conversion specification is invalid, the behavior is undefined.282) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined. |
5 | [...] |
mh schrieb: > Und der Wert wird dann nach int promoted für den Aufruf von printf, > welcher mit einem falschen Conversion Specifier stattfindet... %d wäre hier in allen Fällen korrekt, weil das Argument an printf immer entweder schon vom Typ int ist, oder in diesen promoted wird. Bei %d kommt ja auch in allen Fällen das erwartete Ergebnis raus.
Stefan ⛄ F. schrieb: > Damit muss ich mit dringend im Detail befassen. Sehe ich auch so! Deine Fantasie spielt dir öfter mal Streiche. Ist aber keine Überraschung. Geht wohl jedem mal so. Dein Problem ist nur, dass du das dann VOR jeder Überprüfung, laut und den Wissenden spielend, rausposaunst. Tipp: Einbildung und Realität sind kaum zu unterscheiden, wenn man nur lange genug schon in den "falschen" Bahnen denkt.
mh schrieb: > Wenn ich den Standard richtig verstehe Hast ja recht, habs auch gerade gesehen. Ist allerdings reichlich unwahrscheinlich, dass bei gleicher Grösse was anbrennt, bloss weil die Vorzeichenangabe falsch ist.
:
Bearbeitet durch User
Rolf M. schrieb: > mh schrieb: >> Und der Wert wird dann nach int promoted für den Aufruf von printf, >> welcher mit einem falschen Conversion Specifier stattfindet... > > %d wäre hier in allen Fällen korrekt, weil das Argument an printf immer > entweder schon vom Typ int ist, oder in diesen promoted wird. Bei %d > kommt ja auch in allen Fällen das erwartete Ergebnis raus. Bis C23 gibts noch ne andere Möglichkeit, wenn Compiler/CPU nicht angegeben sind ... Integral Promotion promoted nicht immer zu int, in speziellen Fällen auch unsigned int ;-)
Quellcode zum Nachvollziehen:
1 | #include <stdio.h> |
2 | #include <stdint.h> |
3 | |
4 | int main() |
5 | {
|
6 | puts("8 Bit"); |
7 | uint8_t c=1; |
8 | uint8_t d=0xFF; |
9 | printf("%X + %X = %X (%d bytes)\n",c,d,c+d,sizeof(c+d)); |
10 | printf("%X - %X = %X (%d bytes)\n\n",c,d,c+d,sizeof(c-d)); |
11 | |
12 | puts("16 Bit"); |
13 | uint16_t e=1; |
14 | uint16_t f=0xFFFF; |
15 | printf("%X + %X = %X (%d bytes)\n",e,f,e+f,sizeof(e+f)); |
16 | printf("%X - %X = %X (%d bytes)\n\n",e,f,e+f,sizeof(e-f)); |
17 | |
18 | puts("32 Bit"); |
19 | uint32_t g=1; |
20 | uint32_t h=0xFFFFFFFF; |
21 | printf("%lX + %lX = %lX (%d bytes)\n",g,h,g+h,sizeof(g+h)); |
22 | printf("%lX - %lX = %lX (%d bytes)\n\n",g,h,g+h,sizeof(g-h)); |
23 | |
24 | puts("64 Bit"); |
25 | uint64_t i=1; |
26 | uint64_t j=0xFFFFFFFFFFFFFFFF; |
27 | printf("%llX + %llX = %llX (%d bytes)\n",i,j,i+j,sizeof(i+j)); |
28 | printf("%llX - %llX = %llX (%d bytes)\n",i,j,i+j,sizeof(i-j)); |
29 | }
|
Ausgabe auf PC:
1 | 8 Bit |
2 | 1 + FF = 100 (4 bytes) |
3 | 1 - FF = 100 (4 bytes) |
4 | |
5 | 16 Bit |
6 | 1 + FFFF = 10000 (4 bytes) |
7 | 1 - FFFF = 10000 (4 bytes) |
8 | |
9 | 32 Bit |
10 | 1 + FFFFFFFF = 0 (4 bytes) |
11 | 1 - FFFFFFFF = 0 (4 bytes) |
12 | |
13 | 64 Bit |
14 | 1 + FFFFFFFFFFFFFFFF = 0 (8 bytes) |
15 | 1 - FFFFFFFFFFFFFFFF = 0 (8 bytes) |
Ausgabe auf AVR:
1 | 8 Bit |
2 | 1 + FF = 100 (2 bytes) |
3 | 1 - FF = 100 (2 bytes) |
4 | |
5 | 16 Bit |
6 | 1 + FFFF = 0 (2 bytes) |
7 | 1 - FFFF = 0 (2 bytes) |
8 | |
9 | 32 Bit |
10 | 1 + FFFFFFFF = 0 (4 bytes) |
11 | 1 - FFFFFFFF = 0 (4 bytes) |
12 | |
13 | 64 Bit |
64 Bit kann der printf() auf AVR offenbar nicht. Was man hier dennoch schön sieht ist, dass die Größe des Ergebnisses offenbar nicht immer mit den Operanden identisch ist. Es scheint folgende Regel zu gelten: Das Ergebnis hat immer die Größe der größten Operanden, aber mindestens die Größe des "Standard" Integer. Der ist bei meinem PC 32 Bit groß und bei AVR 16 Bit. Ist das so korrekt?
EAF schrieb: > Dein Problem ist nur, dass du das dann VOR jeder Überprüfung, laut und > den Wissenden spielend, rausposaunst. Ja, du hast Recht. Ich schäme mich dafür.
Stefan ⛄ F. schrieb: > Ist das so korrekt? Schalt erstmal alle passenden Warnungen deines Compilers an und kümmer dich um die Warnungen, die es wahrscheinlich gibt.
Stefan ⛄ F. schrieb: > 64 Bit kann der printf() auf AVR offenbar nicht. Welcher? https://gcc.gnu.org/wiki/avr-gcc#ABI
:
Bearbeitet durch User
mh schrieb: > Integral Promotion promoted nicht immer zu int, in > speziellen Fällen auch unsigned int Ist das nicht technisch identisch? Stefan ⛄ F. schrieb: 8 Bit 1 + FF = 100 (4 bytes) 1 - FF = 100 (4 bytes) Unabhängig ob die 4 Bytes nun int oder uint sind, erwarte ich 0x100 oder 0b100000000. Oder ist das etwa wieder falsch?
Stefan ⛄ F. schrieb: > 64 Bit kann der printf() auf AVR offenbar nicht. (prx) A. K. schrieb: > Welcher? Version 5.4.0
Alle die es noch nicht getan haben (und davon scheint es hier einige zu geben), sollten sich ISO/IEC 9899:2018, Abschnitt 6.3.1 (Arithmetic Operands). (oder wahlweise auch den Draft N2176) zu Gemüte führen. Da sind die Konvertierungen, um die es hier geht, vollständig spezifiziert. Der Abschnitt ist mit zweieinhalb Seiten deutlich kürzer als dieser Thread (und der wir immer länger und länger). Wem das danach immer noch zu kompliziert ist, sollte von C auf Haskell umsteigen. Da gibt es keine impliziten Konvertierungen. Bei Addition, Subtraktion und den meisten anderen arithmetischen Operationen sind die beiden Operanden und das Ergebnis grundsätzlich vom selben Typ. Wer sich dieser Regel widersetzt, wird vom Compiler in seine Schranken verwiesen.
:
Bearbeitet durch Moderator
Yalu X. schrieb: > ISO/IEC 9899:2018, Abschnitt 6.3.1 (Arithmetic Operands). Leider ist das Dokument der Öffentlichkeit (also mir) nicht zugänglich. Der Draft ist dort: https://teaching.csse.uwa.edu.au/units/CITS2002/resources/n2176.pdf
Stefan ⛄ F. schrieb: > mh schrieb: >> Integral Promotion promoted nicht immer zu int, in >> speziellen Fällen auch unsigned int > > Ist das nicht technisch identisch? Nein ist es nicht. > Stefan ⛄ F. schrieb: > 8 Bit > 1 + FF = 100 (4 bytes) > 1 - FF = 100 (4 bytes) > > Unabhängig ob die 4 Bytes nun int oder uint sind, erwarte ich 0x100 oder > 0b100000000. Oder ist das etwa wieder falsch? Ich habe keine Ahnung was du hier fragst. Yalu X. schrieb: > Wem das danach immer noch zu kompliziert ist, sollte von C auf Haskell > umsteigen. Glaubst du, jemand der Integer Promotion in C nicht versteht, kommt mit Haskell klar (also dem Rest der Sprache/Denkweise)?
Stefan ⛄ F. schrieb: > Stefan ⛄ F. schrieb: >> 64 Bit kann der printf() auf AVR offenbar nicht. > > (prx) A. K. schrieb: >> Welcher? > > Version 5.4.0 Ich meine nicht die Version. GCC kann 64-Bit Integer seit Ewigkeiten, aber vielleicht nicht jeder andere Compiler für AVR. In der Doku der avrlibc steht bei printf allerdings kein "%ll..." drin und ein kurzer Blick in dessen Source legt nahe, dass dort der Hase im Pfeffer liegt.
Stefan ⛄ F. schrieb: > Yalu X. schrieb: >> ISO/IEC 9899:2018, Abschnitt 6.3.1 (Arithmetic Operands). > > Leider ist das Dokument der Öffentlichkeit (also mir) nicht zugänglich. Doch, aber du musst halt 198 CHF dafür berappen :) > Der Draft ist dort: > https://teaching.csse.uwa.edu.au/units/CITS2002/resources/n2176.pdf Super, dann hast du ja alle Informationen, die du brauchst, um zukünftigen Überraschungen vorzubeugen :)
(prx) A. K. schrieb: > In der Doku der avrlibc steht bei printf allerdings kein "%ll..." drin > und ein kurzer Blick in dessen Source legt nahe, dass dort der Hase im > Pfeffer liegt. Ich verwende die avr libc 1.2.0
Yalu X. schrieb: > Super, dann hast du ja alle Informationen, die du brauchst, um > zukünftigen Überraschungen vorzubeugen Ja, ich lese gerade. Habe ich mir direkt in meine Büchersammlung kopiert.
Stefan ⛄ F. schrieb: > Leider ist das Dokument der Öffentlichkeit (also mir) nicht zugänglich. Drafts zu beispielsweise C99 oder C11 sind problemlos verfügbar und unterscheiden sich vom Standard nicht signifikant.
Stefan ⛄ F. schrieb: > Es scheint > folgende Regel zu gelten: > > Das Ergebnis hat immer die Größe der größten Operanden, aber mindestens > die Größe des "Standard" Integer. Der ist bei meinem PC 32 Bit groß und > bei AVR 16 Bit. > > Ist das so korrekt? Im Prinzip ja. Wobei es da dann noch ein paar Sonderfälle gibt. (prx) A. K. schrieb: > Stefan ⛄ F. schrieb: >> 64 Bit kann der printf() auf AVR offenbar nicht. > > Welcher? https://gcc.gnu.org/wiki/avr-gcc#ABI Was hat das ABI mit der Implementation von printf() zu tun?
Rolf M. schrieb: > Was hat das ABI mit der Implementation von printf() zu tun? Habe ich auch nicht verstanden. Offenbar ist es eher eine Frage der Implementierung von printf() also der library. Der entscheidende Satz in der Spezifikation ist wohl "If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int." Rolf M. schrieb: > Wobei es da dann noch ein paar Sonderfälle gibt. Kannst du mir Beispiele nennen? Ich frage nach, weil diese Spezifikation nicht gerade leichter Lesestoff ist. Ich habe meinen Kopf leider mit anderen Programmiersprachen voll - wegen der Arbeit. C ist bei mir nur Hobby. (prx) A. K. schrieb: > Drafts zu beispielsweise C99 oder C11 sind problemlos verfügbar und > unterscheiden sich vom Standard nicht signifikant. Ich habe ja jetzt mit Yalus Hilfe den Draft von C17 (Dokument N2176) gefunden.
mh schrieb: > Yalu X. schrieb: >> Wem das danach immer noch zu kompliziert ist, sollte von C auf Haskell >> umsteigen. > > Glaubst du, jemand der Integer Promotion in C nicht versteht, kommt mit > Haskell klar (also dem Rest der Sprache/Denkweise)? Zumindest sind dort alle Regeln sehr einfach gehalten (was sich in der im Vergleich zu C deutlich kürzeren Sprachspezifikation ausdrückt). Außerdem gibt es kaum Überraschungen zur Laufzeit, denn bevor es dazu kommt, erhält man i.Allg. eine Fehlermeldung des Compilers, die recht ausführlich erklärt, warum dieser den ihm vorgesetzten Quellcode nicht umzusetzen kann. Aber natürlich ist es auch in Haskell schwer, Programme zu schreiben. Programmieren (egal in welcher Sprache) fällt niemanden in den Schoß, man muss es erst mit viel Schweiß lernen :)
mh schrieb: > Ich habe keine Ahnung was du hier fragst. Was ich meine ist, dass das Ergebnis als int und uint im Speicher/Register identisch ist.
1 | #include <stdio.h> |
2 | #include <stdint.h> |
3 | |
4 | int main() |
5 | {
|
6 | uint8_t a=0; |
7 | uint8_t b=1; |
8 | |
9 | uint16_t d=a-b; |
10 | int16_t e=a-b; |
11 | |
12 | printf("d = %d = %X (%d bytes)\n",d,d,sizeof(d)); |
13 | printf("e = %d = %X (%d bytes)\n",e,e,sizeof(e)); |
14 | }
|
Ausgabe auf PC:
1 | d = 65535 = FFFF (2 bytes) |
2 | e = -1 = FFFFFFFF (2 bytes) |
Dezimal dargestellt erscheint der unsigned Integer falsch, denn er kann keine negative Zahl darstellen. Nur der signed Integer stellt das Ergebnis richtig dar. Hier gibt mir printf() allerdings zu viele "F" aus. Im Speicher sind es in beiden Fällen die gleichen 2 Bytes, nämlich FFFF.
Hier ist das auch nochmal alles zusammengefasst: https://en.cppreference.com/w/c/language/conversion Stefan ⛄ F. schrieb: > Dezimal dargestellt erscheint der unsigned Integer falsch, denn er kann > keine negative Zahl darstellen. Mathematisch gesehen ja, aber C definiert durchaus, welcher Wert da herauskommt. > Nur der signed Integer stellt das Ergebnis richtig dar. Hier gibt mir > printf() allerdings zu viele "F" aus. Weil hier eine sign extension durchgeführt wird. Ein uint16_t mit dem Wert 65535 behält bei der Konvertierung in einen 32-Bit-Typ diesen Wert bei. Ein int16_t mit dem Wert -1 tut das auch. Allerdings ist die interne Bitrepräsentation dieser beiden Werte zwar vorher gleich, aber nachher unterschiedlich. Im einen Fall müssen die zusätzlichen Bits mit 0 aufgefüllt werden, im anderen mit 1.
Hannes J. schrieb: > Einfach nicht voneinander abziehen oder erst nach dem Vergleich > abziehen: > if(a >= b) { > // Differenz garantiert >= 0 > } else { > // Differenz negativ > } War auch mein erster Gedanke. Die einfachste Lösung. Scheint wohl hier nur keinen zu interessieren.
PittyJ schrieb: > Hannes J. schrieb: >> Einfach nicht voneinander abziehen oder erst nach dem Vergleich >> abziehen: >> if(a >= b) { >> // Differenz garantiert >= 0 >> } else { >> // Differenz negativ >> } > > War auch mein erster Gedanke. Die einfachste Lösung. > Scheint wohl hier nur keinen zu interessieren. Nach dem Motto: "Probleme nicht Lösen, sondern einen Workaround benutzen".
PittyJ schrieb: > Scheint wohl hier nur keinen zu interessieren. Quatsch! Wurde doch sofort akzeptiert. Siehe: Thorsten S. schrieb: > Hannes J. schrieb: >> Einfach nicht voneinander abziehen oder erst nach dem Vergleich >> abziehen: > > Genial, manchmal hat man einen Knoten im Hirn... Das ganze andere Getöse ist nur ein Nebenkriegsschauplatz.
PittyJ schrieb: > War auch mein erster Gedanke. Die einfachste Lösung. > Scheint wohl hier nur keinen zu interessieren. Nunja, die daraus entstandene Diskussion scheint ja für so manchen hier bitter nötig gewesen zu sein.
Rolf M. schrieb: > Nunja, die daraus entstandene Diskussion scheint ja für so manchen hier > bitter nötig gewesen zu sein. Ja genau. Ich finde gut, dass das hier besprochen wurde. Zumindest für mich war es lehrreich.
Stefan ⛄ F. schrieb: > Ja genau. Ich finde gut, dass das hier besprochen wurde. Zumindest für > mich war es lehrreich. Der letzte Durchgang durchs Thema ist noch keine 3 Wochen alt Beitrag "C arithmetic promotion Fallstricke" Oliver
Rolf M. schrieb: > Nunja, die daraus entstandene Diskussion scheint ja für so manchen hier > bitter nötig gewesen zu sein. Für mich auch 😀. Diese Feinheiten sind einem Gelegenheitsprogrammierer meist nicht bewusst und die werde ich mir auch nicht vollständig merken können. Behält man die grundsätzliche Problemstellung wenigstens im Hinterkopf, kommt man auch auf die Lösung von Hannes J. Hannes J. schrieb: > Einfach nicht voneinander abziehen ... oder auf eine andere Formulierung des Vergleichs. Oder sei es auch nur im Fall einer Fehlfunktion einer der ersten Gedanken, an solchen Stellen nochmals nachzudenken.
mh schrieb: >> Die einfachste Lösung. >> Scheint wohl hier nur keinen zu interessieren. > > Nach dem Motto: "Probleme nicht Lösen, sondern einen Workaround > benutzen". Genau DAS ist C typisch. Aber man kann sich dran gewöhnen - wie hier zu sehen ist. W.S.
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.