Zählerüberlauf funktioniert nicht

Hallo,

manchmal teste ich bestimmte Dinge gegen. Heute wollte ich einen Zählerüberlauf nachstellen um die korrekte Differenzbildung zu testen und muss feststellen das die allgemein gängige Methode nicht funktioniert. Und alle anderen Varianten funktionieren auch nicht. Übersehe ich etwas? lastCount bleibt mit 240 stehen. Der automatische Überlauf funktioniert nicht. Würde bedeuten der millis Überlauf funktioniert auch nicht.

Der Klassiker

byte counter;

void setup() {
  Serial.begin(9600);
}

void loop()
{  
  every();   
  counter++;
}

void every()
{
  static byte lastCount = 0;
     
  if (counter - lastCount >= 20)
  {
    lastCount = counter;
    Serial.println("EVENT");
  }

  Serial.print(counter);
  Serial.print('\t');
  Serial.println(lastCount);
}

andere Methode

byte counter;

void setup() {
  Serial.begin(9600);
}

void loop()
{
  every();
  counter++;
}

void every()
{
  static byte lastCount = 0;

  if (counter - lastCount <= 20)
  {
    Serial.print(counter);
    Serial.print('\t');
    Serial.println(lastCount);
    return;
  }

  lastCount += 20;
  Serial.println("EVENT");
}

und eine weitere

byte counter;

void setup() {
  Serial.begin(9600);
}


void loop()
{
  every();
  counter++;
}

void every()
{
  static byte lastCount = 0;

  if (counter - lastCount >= 20)
  {
    lastCount += 20;
    Serial.println("EVENT");
  }

  Serial.print(counter);
  Serial.print('\t');
  Serial.println(lastCount);
}

Vielleicht wird die Rechnung/Vergleich mit int durchgeführt? Wie sieht es aus mit was größerem als byte?

DrDiettrich:
Vielleicht wird die Rechnung/Vergleich mit int durchgeführt?

Natürlich ist das so!
Steht auch so in der C++ Referenz.
Die kleinst mögliche Rechengröße ist int

Abhilfe:

if (byte(counter - lastCount) >= 20)

Würde bedeuten der millis Überlauf funktioniert auch nicht.

Irrtum!
Unsigned long ist größer als int.
Also kein Problem.

Würde bedeuten der millis Überlauf funktioniert auch nicht.

combie:
Irrtum!
Unsigned long ist größer als int.
Also kein Problem.

Ich glaube Doc_Arduino bezog sich auf den, bei ihm nicht so wie erwartet funktionierenden Überlauf mit byte.

grüße Uwe

Ich glaube Doc_Arduino bezog sich auf den, bei ihm nicht so wie erwartet funktionierenden Überlauf mit byte.

Ich habe ihn schon verstanden, glaube ich.

Aber er irrt.
Der Überlauf funktioniert auch mit bytes!!

Mit

counter++;
ist der eindeutige Beweis führbar.

Nur, er rechnet eben nicht, (wie fälschlicherweise angenommen) mit byte, sondern mit int.

Würde bedeuten der millis Überlauf funktioniert auch nicht.

Hier überträgt er sein (vermeintliches) mit Äpfeln gewonnenes Wissen auf Birnen.
So wird der Denkfehler auf eine 2te Ebene transponiert.
Also eher größer, als kleiner.

Hallo,

ja okay, habe nicht an diese Sch... int Rechnung gedacht. Ist eine extreme Stolperfalle.
Danke.

Hallo,

evtl. könnte einer das Problem in 3 Sätzen anfängerfrendlich erklären?
Wenn ich es richtig verstanden habe, wird die ganze Rechnung im int - Zahlenraum durchgeführt,
da nicht jede Variable in dieser Rechnung auch als Byte deklariert ist?

Gruß André

Hallo,

ich versuche es einmal.
Wenn beide Subtrahenden noch nicht übergelaufen sind klappt ja alles.
Dann läuft der counter über und der nächste Event müßte dann bei counter = 4 auslösen wenn lastCount bei 240 steht. Da der µC in int rechnet macht er folgendes
4 - 240 = **-**236
zack, negatives Ergebnis und -236 sind kleiner dem Vergleichswert 20. Also löst hier nichts aus.
Und weil das Ergebnis der Rechnung mit -240 nie mehr größer werden kann als 20, löst am Ende nie wieder etwas aus.

Wenn man jedoch das Ergebnis in den Datenbereich Byte mittels cast zwingt (unsigned), dann sind die -236 gewünschte 20. Serial.println( byte (-236))

Wofür ich noch keine Erklärung habe ist, warum das in größeren Datenbereichen so einfach klappt. Da ist der Compiler schlau genug und schneidet nichts in int ab bevor er das Ergebnis weitergibt.

Er rechnet in int, wenn ihm nichts Größeres vorgegeben wird.

Gruß Tommy

Hallo,

hätte man das nicht einfacher lösen können?
Rechnet in Byte wenn ihm nichts Größeres vorgegeben wurde.
Damit hätte es nie Probleme gegeben.

Edit:
Das mit der int Rechnung werde ich versuchen mir zu merken. :wink:
Danke Euch.

Ich bin mir nicht sicher, ob immer mit int gerechnet wird. AFAIR wird, wie in anderen Programmiersprachen, eine Operation mit gemischten Datentypen mit dem (nächstgrößeren) Datentyp durchgeführt, in den sich beide Operanden verlustfrei konvertieren lassen.

Im konkreten Fall habe ich die Vergleichsoperation im Verdacht, wenn die Konstante als signed betrachtet wird und mit einem unsigned Byte verglichen werden soll. Falls dem so ist, könnte es mit >= 20U funktionieren.

Ist eine extreme Stolperfalle.

Ja.

Leider weiß ich auch nicht, wie man in solchen Fällen wenigstens eine Warnung erzeugt.

Mit:

-pedantic -Wconversion -Wextra -Wall
wird der Kompiler viel gesprächiger, hilft aber nix

Explizite Cast sind sowieso außen vor.
Die sind stumm, oder versagen mit einem Error

Wenn, dann sind implizite Casts das Problem:
Denn verlustfreie Type aufweitungen werden gar nicht gemeldet
Und genau das findet hier (unbeabsichtigt) statt.

Durch einen Umweg könntest du zur gewünschten Meldung kommen:

E:\Programme\arduino\portable\sketchbook\sketch_dec06a\sketch_dec06a.ino: In function 'byte minus(byte, byte)':
E:\Programme\arduino\portable\sketchbook\sketch_dec06a\sketch_dec06a.ino:17:18: warning: conversion from 'int' to 'byte' {aka 'unsigned char'} may change value [-Wconversion]
   17 |   return counter - lastCount;
      |          ~~~~~~~~^~~~~~~~~~~
byte counter;

void setup() {
  Serial.begin(9600);
}

void loop()
{  
  every();  
  counter++;
  delay(100);
}

inline byte  minus(byte counter, byte lastCount)
{
  return counter - lastCount;
};

void every()
{

  static byte lastCount = 0;
  Serial.println(counter - lastCount);
  if(minus(counter,lastCount) >= 20)
  {
    lastCount = counter;
    Serial.println("EVENT");
  }

  Serial.print(counter);
  Serial.print('\t');
  Serial.println(lastCount);
}

Ich verstehe schon, wenn man nicht erwartet, dass da eine stumme Aufweitung passiert, dann schreibt man da auch keinen Wrapper drum rum.

Aber als Tipp:
Beim nächsten Problem welches "irre" erscheint, mit verschiedenen Wrappern umkleiden. Manchmal schreit es einen dann an.

Auch die TypeTraits führen einen dahin.... wenn man ahnt, wo man suchen muss...

#include <CombieTypeMangling.h>

void setup() 
{
  Serial.begin(9600);
  Serial.println(__FILE__);
  auto test = byte(8) - byte(20);
  Serial.println(test);
  Serial.println(is_same<decltype(test),byte>()?"Ist byte":"ist kein byte");
  Serial.println(is_same<decltype(test),int>()?"Ist int":"ist kein int");
}

void loop()
{  
}

Ausgabe:

E:\...blabla... .ino
-12
ist kein byte
Ist int

hätte man das nicht einfacher lösen können?
Rechnet in Byte wenn ihm nichts Größeres vorgegeben wurde.
Damit hätte es nie Probleme gegeben.

Kann man beim Gcc umschalten.
Ist aber dann weit weg vom Standard.

Bedenke 8Bit Rechern sind in der C++ Welt in der Unterzahl
Ganz deutlich.

DrDiettrich:
Falls dem so ist, könnte es mit >= 20U funktionieren.

Gute Idee!
Danke.

E:\Programme\arduino\portable\sketchbook\sketch_dec06b\sketch_dec06b.ino: In function 'void every()':
E:\Programme\arduino\portable\sketchbook\sketch_dec06b\sketch_dec06b.ino:24:26: warning: comparison of integer expressions of different signedness: 'int' and 'unsigned int' [-Wsign-compare]
   24 |   if(counter - lastCount >= 20U)
      |      ~~~~~~~~~~~~~~~~~~~~^~~~~~

Wieso komme ich nicht auf die einfachen Dinge?
:o :o :o :o

Seltsam, Delphi hält sich mit überflüssigen Typkonvertierungen zurück, und warnt bei notwendigen mixed-sign Konvertierungen. Es sollte mich trotzdem wundern, wenn das in C/C++ tatsächlich anders wäre.

Pascal/Delphi ist als Lehrsprache entwickelt worden.

Gruß Tommy

Hallo,

ich wollte soeben schreiben, dass man durchaus an die int Rechnung denken kann wenn man den Trick mit der minus Funktion schreibt. :wink: Das Einfachste mit dem 20U bleibt einem manchmal verborgen wenn man zu tief drin steckt. :slight_smile: Danke an Diettri.

Wegen dem Byte rechnen nochmal. Ich sehe dabei keinerlei Nachteile, auch auf dem PC mit 64Bit Umgebung nicht. Da er ja sowieso mit dem größeren Datentyp automatisch rechnet, wurde ja gesagt. Das Problem gibts ja nur mit kleinerem Datentyp als Standard. Also könnte man den kleinsten Datentyp zum Standard machen und das Problem wäre behoben.

Warum überhaupt einen Datentyp zum Standard für Operationen machen? Sollte das nicht auf den jeweiligen Target-Prozessor abgestimmt werden?

Also könnte man den kleinsten Datentyp zum Standard machen und das Problem wäre behoben.

Du wirst ca 60 Jahre C und C++ Geschichte nicht umwerfen.
(und ich auch nicht)

Der Standard ist "int".
Da hilft kein strampeln, mit den Füßen auf dem Boden stampfen, oder Taschentücher zerknüddeln.

Wegen dem Byte rechnen nochmal. Ich sehe dabei keinerlei Nachteile, auch auf dem PC mit 64Bit Umgebung nicht.

Die fetten Prozessoren tun sich echt schwer mit Byte Berechnungen.
Bis unmöglich.


Dieses mal lesen, dann sieht man, dass das mit den Bytes, an sich schon nicht so ganz einfach ist.
Was ist ein Byte


Warum überhaupt einen Datentyp zum Standard für Operationen machen? Sollte das nicht auf den jeweiligen Target-Prozessor abgestimmt werden?

Int ist im Standard festgelegt!
Als maschinenabhängiger Datentype.

Wie schon gesagt, der avr-gcc besitzt einen Schalter um von 16Bit int auf 8 Bit int umzuschalten.

Also nicht jammern, der Wunsch damit zu arbeiten kann befriedigt werden.
Der Wunsch den Standard zu ändern, die C++ Welt umzukrempeln, wird unerfüllt bleiben.

Die meisten Prozessoren kommen mit 8 bit Bytes (char...) gut klar, PCs weniger mit 16 Bit. Es hat sich noch immer als sehr negativ erwiesen, wenn ein Prozessor nicht gut mit Bytes umgehen kann.

Hallo,

ich nehme es ausnahmsweise :wink: zähneknirschend als Gegeben hin und nein, ich fummel nicht an den Einstellungen rum, wer weiß welchen negativen Rattenschwanz das nach sich zieht, spätestens wenn ich doch mal am PC für den PC programmieren sollte. Dann schlägt die "falsche" Gewohnheit zu. Nach dem Thread hier sollte ich mir das merken können.