Frage zu einer Berechnung

Hallo,

ich habe den Fehler zwar schon behoben,
aber verstehen tu ich es noch nicht!

dieser Code liefert ein korrektes Ergebnis

int vSoStd = vSonnenSekundenHeute / 3600;
int vSoMin = int(vSonnenSekundenHeute - vSoStd * 3600) / 60;
sprintf(sZeit,"%02d:%02d", vSoStd, vSoMin);
datei<<sPrintTime<<vSonnenT<<s<<vSchattenT<<s<<vDelt<<s<<sZeit<<s<<vSonne<<s<<vLUX<<ende;

Dieser Code funktioniert ab der 36000 Sekunde nicht mehr

int vSoStd = vSonnenSekundenHeute / 3600;
int vSoMin = (vSonnenSekundenHeute - vSoStd * 3600) / 60;      //ohne int
sprintf(sZeit,"%02d:%02d", vSoStd, vSoMin);
datei<<sPrintTime<<vSonnenT<<s<<vSchattenT<<s<<vDelt<<s<<sZeit<<s<<vSonne<<s<<vLUX<<ende;

Fehlerhaftes Ergebnis auf meiner SD.

26.06.2019;18:13:23;478;370;108;09:57;1;23645
26.06.2019;18:14:23;477;370;107;09:58;1;23449
26.06.2019;18:15:23;476;370;106;09:59;1;23310              //9Std und 59 Min
26.06.2019;18:16:36;47423692105210:109221223010        //10Std
26.06.2019;18:17:36;47333703103310:109331323010
26.06.2019;18:18:36;47243704102410:109441422548
26.06.2019;18:19:36;4715372599510:109551522279

LG

int geht nur bis 32767. Auf einem "normalen" Arduino.

Vermutlich beantwortet das deine Frage.
Wenn nicht, brauchts mehr Info. Generell ist ohne Variablendefinition (vSonnenSekundenHeute) alles nur Raterei.

dieser Code liefert ein korrektes Ergebnis

Dieser Schnipsel kompiliert nicht mal, egal in welcher Umgebung, weil da eine [ zu viel ist

dieser Code liefert ein korrektes Ergebnis

 int vSoMin = int[(vSonnenSekundenHeute - vSoStd * 3600) / 60;

Glaube ich nicht: Syntax Fehler

Tipp:
Mache dich über den Wertebereich von int kundig.
Sobald du die Grenze über/unter schreitest ......

Hallo,

die Klammer ist natürlich nicht drinnen [

Ich wollte ursprünglich das int rot einfärben, was aber im Quelltext nicht geht.
Beim löschen habe ich dann die Klammer übersehen.

Zur deklaration:

unsigned long vSonnenSekundenHeute = 0;

Eigentlich ist auch nur diese Zeile interessant

int vSoMin = (vSonnenSekundenHeute - vSoStd * 3600) / 60;

da kommt ab vSonnenSekundenHeute = 36001 für vSoMin = 1092 heraus.

Die letzte Zeile streamt das ganze in die Datei mit Streaming.h,

Was bezweckst du mit der Rechnung?

Mir fehlt z.B die Beschreibung von vSoStd und vSoMin...

Für dich mag der Sinn des Ganzen klar sein.
Für mich sieht das alles aus, wie unsortierte Krähen Füße

Und testbaren Code sehe ich auch noch nicht.

Hallo,

was man dir sagen will ist Folgendes. Standardmäßig rechnet der µC mit Datentyp int. Das bedeutet das jede Einzelrechnung auf der rechten Seite einer Gleichung in int passen muss. Wird der Wert größer gibts einen Wertebereichsüberlauf was dazu führt das die Berechnung Müll ist. Den Fehler kann man beheben. Welche Möglichkeit dafür sinnvoll ist kann man dir erst sagen wenn alles weitere bekannt ist. Reduziere meinetwegen den Code auf ein lauffähiges Bsp. was den Fehler zeigt.

void setup(){
  Serial.begin(115200); // Serielle Kommunikation an
  delay(1000);
}

unsigned long vSonnenSekundenHeute = 35990;
void loop(){
  char sZeit[6];
  
  int vSoStd = vSonnenSekundenHeute / 3600;
  //int vSoMin = int(vSonnenSekundenHeute - vSoStd * 3600) / 60;       //geht
  int vSoMin = (vSonnenSekundenHeute - vSoStd * 3600) / 60;             //geht nicht
  sprintf(sZeit,"%02d:%02d", vSoStd, vSoMin);
  vSonnenSekundenHeute++;
  Serial.print(vSonnenSekundenHeute);
  Serial.print("  ");
  Serial.println(sZeit);
  delay(200);
}

Jetzt binn ich aber gespannt, ob das jetzt den Durchbruch bringt?

Jetzt binn ich aber gespannt, ob das jetzt den Durchbruch bringt?

Was für ein Durchbruch?

Der Fehler wurde dir schon mehrfach genannt: Ein Integer Überlauf.
So langsam sollte der Groschen doch fallen.

Hier der reduzierte Beweis Code:

void setup() 
{
 Serial.begin(9600);
 Serial.println("Start");
 unsigned long vSonnenSekundenHeute =  36001;
 int vSoStd = vSonnenSekundenHeute / 3600;
 Serial.println(vSoStd*3600);
}

void loop() 
{

}

Ausgabe:

Start
-29536

Der korrigierte Code:

void setup() 
{
 Serial.begin(9600);
 Serial.println("Start");
 unsigned long vSonnenSekundenHeute =  36001;
 int vSoStd = vSonnenSekundenHeute / 3600;
 Serial.println(vSoStd*3600UL);
}

void loop() 
{

}

Ausgabe:

Start
36000

Ein Integer kippt bei 32767

bei mir kippt es ab bei 36000

Wie erklärst du das?

Wie erklärst du das?

Habe ich doch schon.....

Aber gut, hier dann zum selber mit meißeln:

unsigned long vSonnenSekundenHeute = 36001;
Alles gut, die Zahl passt da rein

int vSoStd = vSonnenSekundenHeute / 3600;
Das Ergebnis erleidet einen impliziten Cast von unsigned long zu int.
Wenn es in vSoStd rein passt, alles gut

vSoStd*3600
Hier dann eine Multiplikation von int mit int.
Ein Überlauf findet statt.
Das Ergebnis ist "Müll"

Die Korrektur:

vSoStd*3600UL
Das Literal 3600 ist jetzt typisiert. Es ist unsigned long.
Für die Multiplikation erfolgt ein implizierter Cast des vSoStd von int zu unsigned long
Die Multiplikation erfolgt unsigned long mit unsigned long.
Ein Überlauf findet NICHT statt.
Das Ergebnis ist "OK"

Doc_Arduino:
Hallo,

was man dir sagen will ist Folgendes. Standardmäßig rechnet der µC mit Datentyp int. Das bedeutet das jede Einzelrechnung auf der rechten Seite einer Gleichung in int passen muss.

Als Mitlesender ist mir hier noch unklar, was du mit "Einzelrechnung" meinst. Das Ergebnis?

Also geht folgendes?

int x = 1000000/1000

Sag ich doch immer wieder : Erst programmieren lernen (respektive die entsprechende Sprache).
Auf einem PC Testprogramme schreiben um zu verstehen.
Erst dann auf so einen armen Microkontroller zustürmen. Mit dem kriegt man eigene Probleme, dann
sollte die Sprache und Programmierung schon “kapiert” sein.

Such mal nach “Formularbreite”. Außerdem ist int immer eine schlechte Idee.
Besser sind die Typen aus <stdint.h> so wie uint8_t, int8_t, uint16_t …

Rechts vom = mit einem anderen Datentypen wie links zu arbeiten und hoffen das das Ergebnis passt ist Chaosprogrammierung.

Ulli

evtl. in diesem Zusammehang lesenswert
Oder auch dieses

Hallo, ihr die auch nicht genau wissen an was es liegt,

mein Name ist Sepp ein Freund von ar182.

Wenn vSoStd <10 ist so ist vSoStd immer <32767 also kein Überlauf ins negative. (9 * 3600 = 32400)

Ist vSonnenSekundenHeute 36000 und größer wird vSoStd 10 und größer.

Also 10 * 3600 = 36000 und somit ein Überlauf ins negative.

Darum hat man hier in diesen speziellen Falle den Überlauf erst bei 36000.

Die Frage war. Warum es erst bei 36000 kippt?

Hallo,

ja das geht, weil du direkt Konstanten verwendest.
int x = 1000000/1000

Was ich sagen wollte ist, wenn du ihn in int rechnen lässt, dann müssen auch alle Zwischenergebnisse in int passen. Zum Bsp. 90006/3 geht schief, weil 90006 den Wertebereich von int verlässt.

@ Sepp:
habt ihr alle einen Sammelaccount?
Setze einmal für 36.000 den Wert 33.000 ein, dann erübrigt sich die Frage "Warum erst ab 36.000?"

Beim Testsketch schreiben bin ich nun allerdings erstaunt das er Dinge richtig rechnet obwohl er falsch rechnen müßte.
Kopf kratz

int a = 9000;
int b = 6;
int c = 3;

void setup(void) {
  Serial.begin(9600);
  unsigned int x = 0;

  x = a*b;
  Serial.println(x);
  
  x = a*b/c;
  Serial.println(x);

  x = a*b*c;
  Serial.println(x);

}

void loop(void) {
  
}
54000        << richtig nicht wie erwartet    
61691        << falsch wie erwartet, im Vergleich mit ersten allerdings seltsam
30928        << falsch wie erwartet

Das erste Ergebnis wundert mich nun doch. Denn er rechnet int mal int. Das das Endergebnis in ein unsigned int soll interessiert ihm an der Stelle noch nicht. Was auch in dem Zusammenhang verwundert ist, wenn er das 1. richtig rechnet, warum ist dann das 2. Ergebnis falsch?

int a = 9000;
int b = 6;
int c = 3;

void setup(void) {
  Serial.begin(9600);
  int x = 0;

  x = a*b;
  Serial.println(x);
  
  x = a*b/c;
  Serial.println(x);

  x = a*b*c;
  Serial.println(x);

}

void loop(void) {
  
}
-11536        << falsch wie erwartet
-3845        << falsch wie erwartet
30928        << falsch wie erwartet

Erst mit den korrigierten unsigned Datentypangaben rechnet er wie ich das erwarte.

unsigned int a = 9000;
int b = 6;
int c = 3;

void setup(void) {
  Serial.begin(9600);
  unsigned int x = 0;

  x = a*b;
  Serial.println(x);
  
  x = a*b/c;
  Serial.println(x);

  x = a*b*c;
  Serial.println(x);

}

void loop(void) {
  
}
54000        << richtig wie erwartet
18000        << richtig wie erwartet
30928        << falsch wie erwartet

Macht man a und x zu long rechnet er alles richtig wie erwartet.

Hallo,

es gibt aber "einen Trick" der bis jetzt immer funktioniert. Man setzt in die Formel ein 1L oder 1UL oder 1.0 (für float) ein. Dann können die restlichen Datentypen bleiben wie sie sind, nur der Datentyp der das Ergebnis aufnimmt muss passen. Das sollte selbsterklärend sein.

int a = 9000;
int b = 6;
int c = 3;


void setup(void) {
  Serial.begin(9600);
  long x = 0;

  x = 1L*a*b;
  Serial.println(x);
  
  x = 1L*a*b/c;
  Serial.println(x);

  x = 1L*a*b*c;
  Serial.println(x);
}

void loop(void) {
  
}
54000
18000
162000

Die Frage war. Warum es erst bei 36000 kippt?

Ist die Antwort denn mittlerweile verstanden?

Beim Testsketch schreiben bin ich nun allerdings erstaunt das er Dinge richtig rechnet obwohl er falsch rechnen müßte.

Logisch rechnet es falsch!
Zumindest ist das Zwischenergebnis negativ
Aber durch den impliziten Cast von int zu unsigned int stimmt es dann wieder.

Beweis mit explizitem Cast:

Serial.println((unsigned int) -11536); // alter Stil
Serial.println(static_cast<unsigned int>(-11536)); // moderner

Hallo,

oh man, der versteckte Cast, genau, daran hatte ich überhaupt nicht gedacht, mit keiner Wimper. Das erklärt alles.
Soviel zum letzten Thema Zeiger "man sollte wissen was unter der Haube passiert". Ich gebe es ja zu. :slight_smile:

Ich hoffe das dem TO bzw. dem Sammelaccount auch alles klar ist. :wink:

der versteckte Cast,

Hier gibts ein semantisches Problem.
Denn versteckt hat da keiner was.
Eher: Der Compiler wurde zu einem impliziten Cast gezwungen.

Tipp:
Ich rate dazu, das Denken zu ändern.
Dann fallen solche Kleinigkeiten evtl. schneller auf.