Hallo zusammen, der Compiler im AVR Studio gibt mir eine Warnung aus, die mir unangemessen erscheint. Was hat es damit auf sich? Erstmal ein Codebeispiel: uint8_t a; uint8_t b; ... belege hier a und b irgendwie ... if(a < a + b){ ... tu irgendwas } Der Compiler bringt jetzt die Warnung: ../usart.c:141: warning: assuming signed overflow does not occur when assuming that (X + c) >= X is always true ../usart.c:141: warning: assuming signed overflow does not occur when assuming that (X + c) < X is always false Demnach soll angeblich das If-Statment immer true ergeben. Wenn ich aber z.B. a=100 und b=200 definiere, sollte der Ausdruck doch false ergeben. Warum bringt der Compiler diese Warnung? (ist doch unschön, wenn man ein Projekt nicht völlig meldungsfrei fertigstellen kann)
Hi, Du hast a und b als unsigned int definiert. Also a und b im Bereich von 0 .. 255 . Also kann a nie kleiner als a + b sein. Gruß Olaf
Olaf Dreyer schrieb:
> Also kann a nie kleiner als a + b sein
Ich würde eher dagegenwetten, das a immer kleiner als a+b ist,
wenn b größer 0.
Die Abfrage entspricht dann:
if(b > 0){
... tu irgendwas
}
Ernst
Emanuel B. schrieb:
> Warum bringt der Compiler diese Warnung?
Was genau willst du denn erreichen?
Mathematisch sollte klar sein, dass für natürliche Zahlen die
Formulierung
a < a + b
immer falsch ist, sofern b nicht 0 ist. Du könntest also glattweg
auch gleich auf b != 0 testen ;-), oder aber du spekulierst darauf,
dass die mathematische Bedeutung durch einen Überlauf von a+b
irgendwie umgangen wird. Das wiederum würde ich mir angesichts
der integer promotion rules von C aber dreimal überlegen.
Also meines Erachtens geht der Compiler an die Auswertung des Ausdrucks a<a+b mit Prioritäten dran. Zuerst wird a+b gelöst und dann das Ergebnis mit a verglichen. Und ich verstehe nicht, warum der Ausdruck immer true ergeben soll. Erst recht scheint mir die Lösung immer false (von Jörg Wunsch) fragwürdig. Zwei Beispiele: (a und b vom Typ uint8_t) a=10, b=20: (a<(a+b)) -> (10<(10+20)) -> (10<30) -> true a=100, b=200: (a<(a+b)) -> (100<(100+200)) -> (100<44) -> false Mein Code im 1. Post war natürlich nur ein Auszug meines Problems. Im Grunde möchte ich folgendes lösen: Ich habe einen 8-Bit-Zahlenbereich, dessen untere Grenze eine uint8_t Variable und dessen obere Grenze die gleiche Variable + eine Konstante ist. Nun möchte ich prüfen, ob eine weitere Zahl in diesem Zahlenbereich liegt, jedoch soll das auch über den Overflow hinweg funktionieren. Beispiel 1: uint8_t a=100; #define konst 100 Damit wird ein Zahlenbereich von 100 bis 200 aufgespannt. Beispiel 2: uint8_t a=200; #define konst 100 Damit wird ein Zahlenbereich von 200 bis 255 und über den Overflow hinweg von 0 bis 44 aufgespannt. Ich möchte mit einem Ausdruck allgemeingültig prüfen, ob eine Testzahl in diesem Zahlenbereich liegt oder nicht. Vielleicht fällt ja jemandem von euch ein Ausdruck ein, in dem nicht a<a+konst vorkommt. Danke euch ;)
Nach den Erweiterungsregeln wird das uint8_t in ein signed int umgewandelt da dieser alle Werte des ursprünglichen Typs fassen kann. Es scheint noch immer so zu sein, das avrgcc ein 16 Bit signed int dafür hernimmt obwohl der "natürliche" Datentyp eher 8 Bit lang wäre. Aber das nur am Rande. Für signed ints aber ist nicht definiert, dass die Arithmetik modulo 2^15 stattfindet. Das ist nur für unsigned ints so definiert (dann natürlich mit 2^16). Ich finde diese Warnung etwas unglücklich formuliert. "when" bezeichnet einen temporalen Zusammenhang und keinen kausalen. Nach den Integer-Regeln aber ist, wie oben schon erwähnt, die Frage ob der Compiler auch bei signed ints Modulo-Arithmetik anwendet implementierungsabhängig. Das aber sollte der Implementierer und Autor der Warnung wissen. Wenn Modulo-Arithmetik angewendet wird, dann kann der Ausdruck bei gewissen Werten von a und b falsch sein. Aber für den Fall das Modulo-Arithmetik nicht angewendet wird, gibt es mehrere Möglichkeiten der Implementerung unter denen einige die Bedingung bei gewissen Werten wahr werden lassen, einige aber garnicht. Das der Ausdruck immer wahr ist kann man nur dann voraussetzen, a) wenn man die Implementierung kennt und diese so gestaltet ist, das der Vergleich von a < a + b immer wahr ist, etwa wenn a + b bei einem Überlauf immer die grösste Zahl (+ Unendlich) darstellen. b) Der Wert von b im Programm immer gleich 0 ist Wenn man das voraussetzt muss man es aber nicht annehmen. Dann braucht man auch nicht warnen, das man annimt, das der Ausdruck immer wahr ist "wenn" die Voraussetzung wahr ist. Sie ist immer wahr. Man muss nur schlicht warnen das die Bedingung immer wahr ist. Das der Ausdruck andererseits manchmal falsch sein wird, kann man dann voraussetzen, wenn einerseits zwar nicht modulo-2^15 gerechnet wird, der Ausdruck aber trotzdem bei bestimmten Werten falsch sein kann. Der kausale Zusammenhang wäre aber, das bei der Berechnung falsch herauskommt, weil die Implementierung für gewisse Werte von a und b dies ergibt obwohl es mathematisch nicht sein kann, weil a und b unsigned 8 Bit sind, aber weil a und b signed ints werden. Insofern würde ich mir wünschen das die Fehlermeldung etwa so lautete: "Condition may be false despite data types causes it to be constantly true due to integer promotion." Emanuel B. schrieb: > Nun möchte ich prüfen, ob eine weitere Zahl in diesem Zahlenbereich > liegt, jedoch soll das auch über den Overflow hinweg funktionieren. Dann deklariere ausdrücklich unsigned ints.
Emanuel B. schrieb: > Nun möchte ich prüfen, ob eine weitere Zahl in diesem Zahlenbereich > liegt, jedoch soll das auch über den Overflow hinweg funktionieren. Aus Neugierde würde ich gerne mal wissen wieso das über den Overflow hinweg funtionieren muss. Was hat das für einen Sinn? Dafür habe ich in knapp 30 Jahren noch nie eine Anwendung gehabt. Es geht doch meist darum zu prüfen ob eine Zahl c in dem Intervall a und a+b liegt. Das macht man so.
1 | if ((c >= a) && (c <= a + b)) |
und spart sich eine Menge Ärger.
Grrrr schrieb: > Dann deklariere ausdrücklich unsigned ints. Wie meinst du das? Soll ich die Ausdrücke pseudo-casten? Ich habe ursprünglich in ASM programmiert und kenne Additionen mit Überläufen als selbstverständlich. Irgendwelche "eigenwilligen" Interpretationen des Compilers sind mir fremd. Daher weiß ich nicht, wie das Problem zu lösen ist. Natürlich kann ich auch anfangen mit Berechnungen, die aber den Quellcode aufblähen. Mein Beispiel von oben könnte ich ja auch mit einer kleinen Rechnung angehen: >Beispiel 2: uint8_t a=200; #define konst 100 >Damit wird ein Zahlenbereich von 200 bis 255 und über den Overflow >hinweg von 0 bis 44 aufgespannt. #define max 255 if(max - a < konst){ //Zahlenbereich läuft über den Overflow hinweg. //Der Zahlenbereich geht von a bis max (wie im Beispiel 200 bis 255) und von 0 bis konst-(max-a)-1 (wie im Beispiel von 0 bis 44) ... } Künstliche Überläufe halte ich aber für unsinnig. Ich muss den Compiler doch zu natürlichen Überläufen zwingen können.
Nachtrag zum Sinn: Grrrr schrieb: > Aus Neugierde würde ich gerne mal wissen wieso das über den Overflow > hinweg funtionieren muss. Was hat das für einen Sinn? Ein Protokoll für eine Datenübertragung soll jedem Datenpaket eine fortlaufende Paketnummer verpassen. Das dient dazu, damit die Gegenstelle anhand dieser Paketnummern Quittungen zurücksenden kann. Die Paketnummern (8 Bit) werden also inkrementiert, auch über den Überlauf hinweg. Es existiert eine FIFO (im Prinzip Ringpuffer), in der die Pakete vor dem Versand abgelegt werden und die weit weniger als 255 Pakete aufnehmen kann. Auch bereits versendete aber noch nicht quittierte Pakete liegen in der FIFO und sobald eine neue Quittung reinkommt, liegen kurzzeitig sogar quittierte Pakete in der FIFO. Wenn ich nun ein Paket aus der FIFO entnehme, muss ich prüfen, ob es bereits quittiert wurde. Das ist der Fall, wenn die Paketnummer nicht im Zahlenbereich von letzteQuittungsNr + FifoGröße liegt. Aber da die Paketnummer und die Quittungsnummer immer "im Kreis" rotieren, muss auch der Zahlenbereich rotieren - eben über den Überlauf hinweg.
Emanuel B. schrieb: > Überläufen als selbstverständlich. Irgendwelche "eigenwilligen" > Interpretationen des Compilers sind mir fremd. Es geht nicht um eigenwillige Interpretationen. Es geht schlicht und ergreifend darum, was die normierte Sprache C hier fordert. Grob gesagt kannst du dir merken: Gerechnet wird immer minimal im Zahlenbereich int. D.h. deine uint8_t werden, gefordert von der Sprachdefinition, zuerst auf int hochgehoben und erst dann wird gerechnet. du kannst zb mittels uint8_t c = a + b; if( a < c ) den Compiler dazu zwingen, das Zwischenergebnis von a + b als uint8_t zu betrachten. Ein if( a < (uint8_t)( a + b ) ) müsste eigentlich den gleichen Effekt erzielen, auch wenn es zunächst wiedersinnig erscheint, da ja alle beteiligten Operanden vom Typ uint8_t sind. Selbst wenn a und b den Datentyp uint8_t haben, so hat das Ergebnis von a + b aber den Datentyp int! Und damit ist dann auch klar, dass dein Ausdruck a < a + b immer true ergeben muss, solange b nur größer als 0 ist. b kann, als uint8_t niemals negativ sein und das Ergebnis von (int)a + (int)b ist daher immer größer/gleich (int)a
Emanuel B. schrieb: > Grrrr schrieb: >> Dann deklariere ausdrücklich unsigned ints. > Wie meinst du das? Soll ich die Ausdrücke pseudo-casten? Nein. Wenn ich "deklarieren" schreibe, dann meine ich das auch so. (K&R, 2. Aufl. S. 204) ;-) Du kannst natürlich auch casten, aber was ist bitte "pseudo-casten"? Emanuel B. schrieb: > Irgendwelche "eigenwilligen" > Interpretationen des Compilers sind mir fremd. Du solltest Dir wirklich mal den K&R anschaffen. Es geht hier nicht um "Eigenwillen" sondern darum das manche Details nach der Sprachbeschreibung implementierungsabhängig sind. Das kann man mögen oder nicht hat aber seine Gründe und man muss damit umgehen (können) ob man will oder nicht. Sorge also dafür das sie Dir nicht mehr länger "fremd" sind Das Problem ist, das bei unsigned ints zwar die arithmetischen Operatoren mit Überlauf arbeiten aber nicht die Vergleichsoperatoren. Das können sie auch nicht. Wie sollte man eine 40 die durch Überlauf entstanden ist von einer "normalen" 40 unterscheiden wenn man schreibt 40 < 100? Dazu müsste der Operator einen zusätzlichen Parameter haben der ihm angibt, ob ein Überlauf stattgefunden hat oder nicht. Aber das gibt Dir auch schon den Hinweis darauf das Du so eine Funktion schreiben kannst. Es geht halt nur nicht mit einem einfachen Ausdruck, denn nach der Überlaufenden bzw. "Unterlaufenden" Addition bzw. Subtraktion ist die Information nicht mehr da. Nur noch der Zahlenwert. Du musst vorher testen ob es einen Überlauf gibt. Allerdings wäre die offensichtliche und besser zu verstehende Methode die beiden Intervalle getrennt zu testen. Ist letztlich auch der selbe Aufwand, denke ich.
Mist. Jetzt habe ich so lange dran geschrieben und überlegt wie ich es dennn meinem Kinde sage, das Karl Heinz schneller war.
Grrrr schrieb: > Mist. Jetzt habe ich so lange dran geschrieben und überlegt wie ich es > dennn meinem Kinde sage, das Karl Heinz schneller war. Ich denke, wir haben zwar von ähnlichen aber doch anderen Dingen geredet. Der zentrale Punkt jedoch ist derselbe: Um solche 'Feinheiten' zu verstehen, ist es unumgänglich das Lernen durch "Try & Error" endgültig ad acta zu legen und sich endlich Literatur zu beschaffen und durchzuarbeiten. Alles andere ist auf lange Sicht gesehen immer nur Halbwissen und dann kommt es genau zu solchen schwer zu verstehenden Irrtümern, die dann als eigenwillige Interpretationen des Compilers aufgefasst werden.
:-))))))))))) Open output to "Karl Heinz" Command Meta "Geh schlafen" Close output Comment Ich gehe jetzt auch. Übrigens: Mein "K&R" ist neben dem "Holleman Wieberg" und dem "Tietze Schenk" eines der wenigen die in Umschlagfolie geschützt sind. Sie haben es auch nötig.
Grrrr schrieb: > :-))))))))))) > > Open output to "Karl Heinz" > Command Meta "Geh schlafen" > Close output > Comment Ich gehe jetzt auch. while( ! isSleeping ) sheep++;
Grrrr schrieb: > Nein. Wenn ich "deklarieren" schreibe, dann meine ich das auch so. (K&R, > 2. Aufl. S. 204) ;-) Du kannst natürlich auch casten, aber was ist bitte > "pseudo-casten"? Was genau soll die "eigentliche" Lösung sein? Wenn du schreibst "du kannst natürlich auch casten", dann muss es ja noch eine bevorzugte Lösung geben. Mit "pseudo-casten" meinte ich natürlich casten, aber ich caste ja von einem Typ zum selben, nur um den Compiler an einer Typänderung zu hindern. Das ist nicht der eigentliche Sinn vom Casting. Und ja, ich hab das mit der Konvertierung zum int verstanden, aber äußerlich (wenn ich dem Compiler nicht unter die Haube schaue), muss ich zum selben Typ casten. Grrrr schrieb: > Wie sollte man eine 40 die durch Überlauf > entstanden ist von einer "normalen" 40 unterscheiden wenn man schreibt > 40 < 100? Dazu müsste der Operator einen zusätzlichen Parameter haben > der ihm angibt, ob ein Überlauf stattgefunden hat oder nicht. Nö, 40 ist 40. Es liegt doch in der Natur der Sache, dass es nicht zwei verschiedene 40 geben kann. Bei einem Überlauf entsteht eben immer ein Informationsverlust, da die nächsthöheren Stellen verlorengehen. Immer dann, wenn man mit Überläufen und Unterläufen zu tun hat, nimmt man diese Tatsache in Kauf. Wenn ich eine 40 mit einem Überlaufflag versehen würde, hab ich den Informationsverlust nicht in Kauf genommen und stillschweigend einen 9Bit-Speicher geschaffen. Ich möchte aber mit 8 Bit arbeiten. Und genauso ist es ja bei meinem Beispiel. Wenn ich einen Zahlenbereich rotiere, ist 40 immer gleich 40 und mich interessiert dabei nicht, wieviele Rotationen oder Überläufe zu dieser Zahl geführt haben. Jede Zahl ist aus einer eindeutigen Rechnung reproduzierbar entstanden.
Emanuel B. schrieb: > Was genau soll die "eigentliche" Lösung sein? Wenn du schreibst "du > kannst natürlich auch casten", dann muss es ja noch eine bevorzugte > Lösung geben. Mit "pseudo-casten" meinte ich natürlich casten, aber ich > caste ja von einem Typ zum selben, nur um den Compiler an einer > Typänderung zu hindern. Das ist nicht der eigentliche Sinn vom Casting. > Und ja, ich hab das mit der Konvertierung zum int verstanden, aber > äußerlich (wenn ich dem Compiler nicht unter die Haube schaue), muss ich > zum selben Typ casten. Das war ja sowieso nur ein Seitenzweig der Argumentation. Es gibt keine "eigentliche" Lösung, die Dir erlaubt es so zu machen, wie Du es ursprünglich beabsichtigt hast. Den Grund habe ich Dir schon geschrieben, aber ich wiederhole ihn nochmal. Alles was kleiner ist als ein int wird vom Compiler (genauer dem avrgcc) in ein signed int umgewandelt. Für diese ist im C-Standard keine Modulo-Arithmetik gefordert und bei avrgcc auch nicht implementiert. Daher kannst Du keine Programme schreiben, die sich darauf verlassen, das ein Überlauf bei 2^8-1 oder 2^15 geschieht. Das ist einfach so. Emanuel B. schrieb: > Nö, 40 ist 40. Es liegt doch in der Natur der Sache, dass es nicht zwei > verschiedene 40 geben kann. Bei einem Überlauf entsteht eben immer ein > Informationsverlust, da die nächsthöheren Stellen verlorengehen. Immer > dann, wenn man mit Überläufen und Unterläufen zu tun hat, nimmt man > diese Tatsache in Kauf. Wenn ich eine 40 mit einem Überlaufflag versehen > würde, hab ich den Informationsverlust nicht in Kauf genommen und > stillschweigend einen 9Bit-Speicher geschaffen. Ich möchte aber mit 8 > Bit arbeiten. Das eben geht nicht. Punkt. Du kannst es leugnen, aber das hilft Dir nicht. Es ist ein Unterschied ob Du eine 40 die durch Überlauf entstanden ist testen willst oder eine 40 die ohne Überlauf entstanden ist. Versuche einfach mal, wie ich Dir empfohlen habe, die Intervalle einzeln zu testen, dann wird Dir das klarer. Emanuel B. schrieb: > Jede Zahl ist aus einer eindeutigen Rechnung reproduzierbar > entstanden. Das ist hier garnicht die Frage. Die Frage ist ob es eine eineindeutige Beziehung gibt. Die gibt es nicht. Wie gesagt, es ist einer 40 nicht anzusehen, ob sie durch Überlauf entstanden ist oder nicht. Aber das führt alles zu nichts. Du kannst schreien, weinen oder Deinem Bundestagsabgeordneten schreiben. Die Mathematik bleibt die gleiche. Überlege doch mal Folgendes: In einem Ring (genauer einem Restklassenring) sind die Relationen a < b und a > b mit a != b immer gleichzeitg wahr. Wieso ist das so? Weil jedes Element alle anderen Elemente als Nachfolger und als Vorläufer hat. D.h. für jedes a aus dem Ring gilt das jedes von ihm verschiedene b kleiner und grösser zugleich ist. Oder noch anders ausgedrückt: Bei a != b ist b sowohl der Nachfolger von a und dessen Vorgänger. Jetzt schlaf darüber und morgen liest Du Dir das noch mal in Ruhe durch.
Grrrr schrieb: > Es scheint noch immer so zu sein, das avrgcc ein 16 Bit signed int dafür > hernimmt obwohl der "natürliche" Datentyp eher 8 Bit lang wäre. Aber das > nur am Rande. Weil der C-Standard vorschreibt, dass ein int mindestens 16 Bit haben muss.
also wenn ich mir deine if anweisung Anschaue, dann soll sie genau dann True ergeben, was sie eben nicht tut wegen benannter typerweiterung, wenn b>255-a ist, oder irre ich mich da? dann schreib das doch so hin! if(b > 255-a ) { ... } hier gibts keine Überläufe, und das Ergebnis sollte das selbe sein
sorry falsch rum, jetzt hab ich mich so darauf versteift rauszukriegen wann die anweisung false ergibt, dass ich dass dann als Bedingung genommen habe. also if(!(b > 255-a)) oder if (b <= 255-a)
Danke euch für eure zahlreichen Antworten! Grrrr schrieb: > Überlege doch mal Folgendes: In einem Ring (genauer einem > Restklassenring) sind die Relationen a < b und a > b mit a != b immer > gleichzeitg wahr. Fein, jetzt kommst du der Sache auf die Spur. Du hast beide Relationen als gleichzeitig gültig dargestellt (mit Ausnahme a==b) UND du hast in beiden Relationen das gleiche a und das gleiche b verwendet. Demnach bestätigst du doch damit, dass eine 40 immer eine 40 ist. Grrrr schrieb: > Weil jedes Element alle anderen > Elemente als Nachfolger und als Vorläufer hat. Ebenfalls hier: Du hast ja auch recht mit der Aussage, aber du sagst, dass ein Element nicht sein eigener Nachfolger oder Vorläufer sein kann, sonder dass das nur für alle anderen Elemente zutrifft. Das geht ja auch auf die Grundvoraussetzung zurück, dass jedes Element im Ring nur einmal vertreten ist. Demnach ist die 40 eine 40, weil sie nicht ihr eigener Nachfolger/Vorläufer sein kann. Wäre es relevant, ob man durch einen Überlauf oder nicht zu einer 40 kommt, hätten wir es nicht mit einem Ring, sondern mit einer Spirale (entlang der senkrechten Mittelpunktachse des Rings) zu tun, die ebenfalls die 8-Bit Zahlen rotiert, aber dann gibt es mit 40+k*2^8 (k Element von Z) unendlich viele 40 und es ist relevant um welche 40 es sich handelt um einen Größer-/Kleiner-Vergleich anstellen zu können. Dies ist aber hier nicht der Fall. Grrrr schrieb: > Wie gesagt, es ist einer 40 nicht > anzusehen, ob sie durch Überlauf entstanden ist oder nicht. Es muss ihr auch nicht anzusehen sein, denn die 40 ist nur ein Wert und nicht zugleich dessen Historie ;) Falls das relevant ist, muss mein Programm diese Entstehungsinformationen eben verarbeiten oder es müssen Flags aus der CPU ausgelesen werden. Diese sind jedoch nicht an die Zahl sondern an die letzte Berechnung gekoppelt. Norbert schrieb: > if (b <= 255-a) Danke für den Hinweis in dieser Richtung. Ich kann deinen Gedankengang nachvollziehen. Leider ist das nicht das gleiche, aber eine äquivalente Lösung finde ich gerade auch nicht. Für a=0 und b=0 liefert dein Ausdruck true, aber meiner (a < a+b) liefert false. In meiner Programmlogik wäre der Fall nicht relevant, da b in meinem Fall eine Konstante ist, die >0 sein muss. Dennoch würde mich ein Äquivalent in dieser Hinsicht interessieren. Grrrr schrieb: > Alles was kleiner ist als ein int wird vom Compiler (genauer dem avrgcc) > in ein signed int umgewandelt. Ist das bei einem 8Bit µC sinnvoll? Eine 16 Bit Operation benötigt mehrere Takte auf einem AVR, da ja nur mit 8 Bit gerechnet wird und mehrere Speicherzugriffe durchgeführt werden müssen. Also selbst wenn ich zwei 8 Bit Zahlen addiere, willst du sagen, dass der Compiler dem AVR eine 16 Bit Berechnung aufzwingt und anschließend alle Ergebnisse über das zu speichernde 8 Bit Ergebnis hinaus verworfen werden? Wo ist da die Effizienz? Nochmal ein Danke an alle, besonders an Grrrr für seinen Aufwand.
Emanuel B. schrieb: > Also selbst wenn > ich zwei 8 Bit Zahlen addiere, willst du sagen, dass der Compiler dem > AVR eine 16 Bit Berechnung aufzwingt und anschließend alle Ergebnisse > über das zu speichernde 8 Bit Ergebnis hinaus verworfen werden? Wo ist > da die Effizienz? Ja, aber der Optimierer entfernt wieder etliches von den Integer-Promotions, so dass es sich meist nur intern beim Compilieren abspielt und sich nicht im endgültigen Code wiederfindet.
Der Standard verlangt nicht, dass jede Rechnung mit int ausgeführt wird. Der Standard verlangt, dass das Ergebnis so aussieht, als wenn es mit int berechnet worden wäre. Das heißt, wenn es zum gleichen Ergebnis führt, dann rechnet der Compiler auch nur in 8 Bit (solange die Optimierung eingeschaltet ist).
Stefan Ernst schrieb: > Ja, aber der Optimierer entfernt wieder etliches von den > Integer-Promotions, so dass es sich meist nur intern beim Compilieren > abspielt und sich nicht im endgültigen Code wiederfindet. Aber bei meinem Ausdruck (a<a+b) wird vom µC trotzdem ein 16 Bit Ergebnis aus a+b (je 8 Bit) berechnet und das einzelne a auch auf 16 Bit aufgebohrt und dann beides verglichen?
Emanuel B. schrieb: > Stefan Ernst schrieb: >> Ja, aber der Optimierer entfernt wieder etliches von den >> Integer-Promotions, so dass es sich meist nur intern beim Compilieren >> abspielt und sich nicht im endgültigen Code wiederfindet. > > Aber bei meinem Ausdruck (a<a+b) wird vom µC trotzdem ein 16 Bit > Ergebnis aus a+b (je 8 Bit) berechnet und das einzelne a auch auf 16 Bit > aufgebohrt und dann beides verglichen? Ja. Weil in diesem Fall das 16 Bit Ergebnis nicht identisch ist zum 8 Bit Ergebnis, wie du selber feststellen musstest.
Was der Compiler aus dem Statement macht ist if(1){ ... tu irgendwas }
> Ist das bei einem 8Bit µC sinnvoll?
Darum gehts nicht.
C Compiler laufen auf Maschinen, von 8 Bit bis hoch zu 64 Bit und
wahrscheinlich noch höheren Bitzahlen, von deren Existenz hier noch nie
jemand etwas gehört hat.
Wenn man haben will, dass sich Programme quer über all diese Plattformen
einigermassen gleich verhalten und portierbar sind, dann muss man
gewisse Regeln festlegen. Für manche Plattformen sind diese Regeln gut,
für die anderen sind sie schlecht.
Die C Regeln sind zum großen Teil so gewählt, dass man auf den meisten
Plattformen in vielen Fällen damit leben kann. Aber manchmal zahlt man
auch drauf.
Emanuel B. schrieb: > Fein, jetzt kommst du der Sache auf die Spur. Ach! :-) Emanuel B. schrieb: > Du hast beide Relationen > als gleichzeitig gültig dargestellt (mit Ausnahme a==b) UND du hast in > beiden Relationen das gleiche a und das gleiche b verwendet. Demnach > bestätigst du doch damit, dass eine 40 immer eine 40 ist. Bestätigen tue ich das nicht. Ich habe in meiner Aussage über die < und > Relation in dem Ring den Fall ausgenommen das beide Elemente identisch sind. Logisch gesehen mache ich damit keine Aussage über Identität. Du drehst Dich im Kreis. Grrrr schrieb in Beitrag "Re: AVR Studio - Was soll diese Compiler-Warnung?": > Wie sollte man eine 40 die durch Überlauf > entstanden ist von einer "normalen" 40 unterscheiden wenn man schreibt > 40 < 100? Meine eigenen Aussagen brauchst Du mir nicht zu beweisen, wenn ich ohnehin überzeugt bin, dass sie wahr sind. Es gerade umgekehrt und so habe ich es auch hier geschrieben. Du hast es sogar selbst bestätigt in Emanuel B. schrieb: > Wenn ich eine 40 mit einem Überlaufflag versehen > würde, hab ich den Informationsverlust nicht in Kauf genommen und > stillschweigend einen 9Bit-Speicher geschaffen. Man muss die beiden 40iger unterscheiden können! Du setzt einen Informationsverlust voraus, wie ich. > Ich möchte aber mit 8 > Bit arbeiten. Und genauso ist es ja bei meinem Beispiel. Aber den magst Du nicht. Nein! Das soll nicht sein! Damit wir weiterkommen, sag mir doch mal bitte ob das soweit klar ist, das Du mit uint8_t keinen Überlauf hinkriegst. Ich mache derweil mal eine Skizze. Aber es kann sehr spät werden. Muss noch ein bisschen Geld verdienen.
Damit die Zeit nicht so lang wird: Der Vergleich a < b ist dann und nur dann wahr wenn a im Bereich der kleinsten Zahl und b liegt. Der Vergleich a > b ist dann und nur dann wahr wenn a im Bereich von b und der grössten Zahl liegt.
Grrrr schrieb: > Du drehst Dich im Kreis. Ja genau, das ist bei einem Ring so vorgesehen ;) Grrrr schrieb: > Man muss die beiden 40iger unterscheiden können! Du setzt einen > Informationsverlust voraus, wie ich. > >> Ich möchte aber mit 8 >> Bit arbeiten. Und genauso ist es ja bei meinem Beispiel. > > Aber den magst Du nicht. Nein! Das soll nicht sein! Doch, ich WILL den Informationsverlust, da es ja sonst nicht bei dem Ring bleibt. Ich habe 8 Bit Informationsgehalt, damit lässt sich eine 40 nur auf eine Weise darstellen, vorausgesetzt man verwendet die kompletten 8 Bit für den Wertebereich und nicht nur zB 6 Bit als Wertebereich und noch zwei Flags. Auch der Compiler sieht eine 40 als 40, so habt ihr mir das doch beschrieben. Wenn ich zB folgenden Ausdruck prüfe, dann gibt es nur eine 40, auch wenn 200+96 auf 8 Bit gecastet ebenfalls 40 ergibt: (40 < 200 + 96) der Compiler verursacht, dass die Zahlen in die Register des µC geladen werden und auf 16 Bit verbreitert werden. Demnach wird der Ausdruck mit 16 Bit untersucht (40<296) und liefert true. Es gibt nur eine 40, egal wie es dazu kam. Grrrr schrieb: > Damit wir weiterkommen, sag mir doch mal bitte ob das soweit klar ist, > das Du mit uint8_t keinen Überlauf hinkriegst. Ja, das hab ich dank euch verstanden. Mitlerweile habe ich mir eine kleine Funktion gemacht. Es ging ja ursprünglich darum zu prüfen, ob eine Zahl in einem Zahlenbereich liegt, der einen Overflow enthalten kann.
1 | //ISINSEGMENT |
2 | //Prüft, ob sich eine Zahl innerhalb eines Zahlenbereichs mit Unter- und Obergrenze (je einschließlich) befindet und gibt dann 1 zurück, sonst 0. Der Zahlenbereich wird als Ring angesehen und kann über einen 8-Bit-Overflow hinweg verlaufen, was berücksichtigt wird. Ist beim Aufruf upper<lower, dann ist ein Overflow enthalten. |
3 | uint8_t isinsegment(uint8_t lower, uint8_t upper, uint8_t number){ //beim Aufruf muss gelten: lower!=upper. |
4 | if(lower < upper){ |
5 | //Kein Überlauf zwischen den Bereichsgrenzen |
6 | if(number >= lower && number <= upper) return 1; else return 0; |
7 | } else if(upper < lower){ |
8 | //Überlauf zwischen den Bereichsgrenzen |
9 | if(number >= lower || number <= upper) return 1; else return 0; |
10 | } |
11 | |
12 | return 0; |
13 | } |
Also guck mal. Das Bild soll erkennbar machen, das die Vergleichsoperatoren in C nicht implizit mit einem Überlauf rechnen. Sie nehmen an das ihre Zahlenbereiche zu immer grösseren bzw kleineren Elementen reichen. Mit dem Vergleich x > 180 kriegt man nur die Information das x > 180 und kleiner gleich 255 ist (denn weiter geht ein uint8 nicht) Genauso ergibt der Vergleich x < 40 nur die Information das x zwischen 0 und 40 liegt. Du brauchst also mindestens drei Variablen: Untergrenze, Obergrenze und x, sowie mindestens zwei Vergleiche und eine logische Verknüpfung für den Fall das der zu testende Bereich 0 und 255 überstreicht. Vielleicht kannst Du ja versuchen mir anhand des Bildes zu erklären mit welchen Vergleichen und Rechnungen es gehen soll. Inzwischen hast Du schon geantwortet: Emanuel B. schrieb: > Doch, ich WILL den Informationsverlust, da es ja sonst nicht bei dem > Ring bleibt. Wenn Du den Informationsverlust in Kauf nimmst, kannst Du die Entscheidung nicht treffen! Also darfst Du ihn nicht in Kauf nehmen! Emanuel B. schrieb: > Mitlerweile habe ich mir eine kleine Funktion gemacht. Es ging ja > ursprünglich darum zu prüfen, ob eine Zahl in einem Zahlenbereich liegt, > der einen Overflow enthalten kann. Diesen Code meinte ich. Siehst Du nun warum es nicht mit einem einfachen Ausdruck geht?
Man könnte es auch so sehen: Uhr Ist es, wenn der große Zeiger auf 15 steht nun früher oder später als wie wenn er auf 20 steht. Die Antwort ist: Es kommt drauf an 17:15 ist früher als 17:20. Aber 18:15 ist später :-) Oder um das Beispiel etwas näher an den vorliegenden Fall zu bringen. großer Zeiger auf 58 großer Zeiger auf 02 Liegen die beiden Zeitpunkte weniger als 5 Minuten auseinander? Kann man nicht entscheiden. Wenn es sich um 17:58 und 18:02 handelt, dann ja. Hat man es aber mit 17:02 und 17:58 zu tun, dann nein. Folgerung: Nur mit der Kentniss der Minuten kann diese Frage nicht entschieden werden. Irgendeine zusätzliche Information oder Annahme benötigt man noch.
Emanuel B. schrieb: > Norbert schrieb: >> if (b <= 255-a) > > Danke für den Hinweis in dieser Richtung. Ich kann deinen Gedankengang > nachvollziehen. Leider ist das nicht das gleiche, aber eine äquivalente > Lösung finde ich gerade auch nicht. Für a=0 und b=0 liefert dein > Ausdruck true, aber meiner (a < a+b) liefert false. if (b <= 256 - a)
Grrrr schrieb: > Diesen Code meinte ich. Siehst Du nun warum es nicht mit einem einfachen > Ausdruck geht? Genau dieser Code ist aber der gleiche Ausdruck aus meinem ursprünglichen Post, denn so habe ich das Problem schon die ganze Zeit zu lösen versucht. Nur da ich den Krempel jetzt in eine Funktion verfrachtet habe, anstatt wie vorher in einen kombinierten Ausdruck, wird bei der Parameterübergabe heimlich gecastet. Hier nochmal die gleiche Funktion von oben. Für lower setze ich im Prinzip a und für upper setze ich a + KONST ein. Die Zeile "if(lower < upper){" produziert rein inhaltlich also wieder den Ausdruck (a<a+KONST). Der Unterschied liegt hier nur darin, dass ich im Prinzip gecastet habe, dadurch dass der Wert a+KONST beim Funktionsaufruf bereits berechnet und auf 8 Bit beschnitten wurde und anschließent ein Vergleich mit dem bereits berechneten 8 Bit Wert durchgeführt wird.
1 | //ISINSEGMENT |
2 | //Prüft, ob sich eine Zahl innerhalb eines Zahlenbereichs mit Unter- und Obergrenze (je einschließlich) befindet und gibt dann 1 zurück, sonst 0. Der Zahlenbereich wird als Ring angesehen und kann über einen 8-Bit-Overflow hinweg verlaufen, was berücksichtigt wird. Ist beim Aufruf upper<lower, dann ist ein Overflow enthalten. |
3 | uint8_t isinsegment(uint8_t lower, uint8_t upper, uint8_t number){ //beim Aufruf muss gelten: lower!=upper. |
4 | if(lower < upper){ |
5 | //Kein Überlauf zwischen den Bereichsgrenzen |
6 | if(number >= lower && number <= upper) return 1; else return 0; |
7 | } else if(upper < lower){ |
8 | //Überlauf zwischen den Bereichsgrenzen |
9 | if(number >= lower || number <= upper) return 1; else return 0; |
10 | } |
11 | |
12 | return 0; |
13 | } |
Wie gesagt hatte ich vorher genau den gleichen Ausdruck, der oben in der Funktion steckt, nur als einen kombinierten Ausdruck und dabei hat der Compiler diese Warnung erzeugt. Nur zum zeigen:
1 | if((lower < upper && number >= lower && number <= upper) || (upper < lower && (number >= lower || number <= upper))){ |
2 | ... |
3 | } |
Karl heinz Buchegger schrieb: > Ist es, wenn der große Zeiger auf 15 steht nun früher oder später als > wie wenn er auf 20 steht. > > Die Antwort ist: Es kommt drauf an > 17:15 ist früher als 17:20. Aber 18:15 ist später :-) Hatten wir jetzt schon ein paarmal in grün aber: Nein, die Antwort ist Wenn der große Zeiger auf 15 steht, ist es immer früher als wenn er auf 20 steht. Mehr Information steckt in einem Ring nicht drinne. Sobald du mit einen Stundenzeiger weitere Information einbeziehst, wird dein Ring zu einer Spirale, also im Grunde ganz viele gestapelte Ringe und wenn ein Ring durchlaufen wurde, springst du zum nächsten. Dann wäre tatsächlich unklar, ob die "15" nun vor oder nach der "20" kommt, weil es mehrere bzw. unendlich viele "15" und "20" gibt. Jörg Wunsch schrieb: > if (b <= 256 - a) Das ist ein Test, oder? Das kann nicht funktionieren genau aus dem Grund, um den es hier die ganze Zeit geht. Sei a=0 und b=0. Der Compiler berechnet den Ausdruck wieder auf 16 Bit und führt dann den Vergleich 0<=256 durch, was true ergibt. Mein Ausdruck lieferte für a=0 und b=0 jedoch false.
Emanuel B. schrieb: >> if (b <= 256 - a) > > Das ist ein Test, oder? Natürlich nicht. Nur nicht richtig nachgedacht. > Der Compiler > berechnet den Ausdruck wieder auf 16 Bit und führt dann den Vergleich > 0<=256 durch, was true ergibt. Mein Ausdruck lieferte für a=0 und b=0 > jedoch false. Dann caste deine Terme explizit auf (unsigned). Aber ich gebe zu, du hast mich in den länglichen Ausführungen irgendwann mental verloren. Den Test mit "ist es früher oder später auf der Uhr" haben wir hier auch schon einmal benötigt, und wir sind zu dem Schluss gekommen, dass es nicht ohne Fallunterscheidung geht. Wenn du wissen willst, ob 04.23 Uhr nun früher oder später ist als 16.21 Uhr ohne dabei zu wissen, ob mittlerweile schon der nächste Tag ist (dann wäre 04.23 Uhr nämlich später), dann musst du gucken, in welche Richtung die Differenz zwischen beiden kürzer ist. Ich bin mir aber nach den vielen Worten, die hier schon gepostet worden sind, nicht mehr im Klaren, ob das nun eigentlich dein Prolem war/ist oder nicht. > Nur da ich den Krempel jetzt in eine Funktion > verfrachtet habe, anstatt wie vorher in einen kombinierten Ausdruck, > wird bei der Parameterübergabe heimlich gecastet. Es wird nicht "heimlich gecastet", aber das Verhalten einer Funktion ist in der Tat nicht identisch zu einem Ausdruck. Die integer promotion rules von C sind leider nichts, was man durch trial & error erfassen kann. Die muss man mal verstanden haben, und im Gegensatz zu anderen Dingen (bspw. der formalen Definition des type qualifiers "restrict") kann man die integer promotion sogar aus dem Text des Standards begreifen, wenn man sich sie nur ein paar Mal zu Gemüte führt.
Was soll auch diese Diktion "heimlich" und "im Prinzip" casten? Wenn der grosse Zeiger auf 20 steht ist es immer später als wenn er auf 15 steht? Heiligs Blechle! Der kann ja nicht mal die Uhr ablesen. Das wird mir jetzt echt zu schwurbelig. Ich sage ihm: Das ist nicht das selbe. Darauf er: Das ist das selbe. Das kann ja noch Jahre so weiter gehen. Ich geb's auf. Wenn er es halt nicht verstehen will und meint durch pure Willenskraft sowohl die Mathematik als auch den C-Standard umwerfen zu können... Ich helfe ihm dabei nicht. Ich hab's mit dem Rücken. ;-)
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.