Forum: Compiler & IDEs AVR Studio - Was soll diese Compiler-Warnung?


von Emanuel B. (movergan)


Lesenswert?

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)

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?

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

von Ernst (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Emanuel B. (movergan)


Lesenswert?

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

von Grrrr (Gast)


Lesenswert?

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.

von Grrrr (Gast)


Lesenswert?

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.

von Emanuel B. (movergan)


Lesenswert?

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.

von Emanuel B. (movergan)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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

von Grrrr (Gast)


Lesenswert?

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.

von Grrrr (Gast)


Lesenswert?

Mist. Jetzt habe ich so lange dran geschrieben und überlegt wie ich es 
dennn meinem Kinde sage, das Karl Heinz schneller war.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Grrrr (Gast)


Lesenswert?

:-)))))))))))

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.

von Grrrr (Gast)


Lesenswert?

Ach und natürlich der "Aho".

von Karl H. (kbuchegg)


Lesenswert?

Grrrr schrieb:
> :-)))))))))))
>
> Open output to "Karl Heinz"
> Command Meta "Geh schlafen"
> Close output
> Comment Ich gehe jetzt auch.

  while( ! isSleeping )
    sheep++;

von Grrrr (Gast)


Lesenswert?


von Emanuel B. (movergan)


Lesenswert?

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.

von Grrrr (Gast)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Norbert (Gast)


Lesenswert?

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

von Norbert (Gast)


Lesenswert?

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)

von Emanuel B. (movergan)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Klaus (Gast)


Lesenswert?

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

von Emanuel B. (movergan)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Was der Compiler aus dem Statement macht ist
  if(1){
  ... tu irgendwas
  }

von Karl H. (kbuchegg)


Lesenswert?

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

von Grrrr (Gast)


Lesenswert?

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.

von Grrrr (Gast)


Lesenswert?

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.

von Emanuel B. (movergan)


Lesenswert?

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
}

von Grrrr (Gast)


Angehängte Dateien:

Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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)

von Emanuel B. (movergan)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Grrrr (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.