Kurze Erklärung zu int mit unsigned long Rechnung

Hallo.
Kann mir kurz jemand erklären ob das wirklich stimmt was mir ChatGPT gesagt hat?
Stimmt diese Rechnung:

unsigned long var1 = 100;
var1 += 86400UL;

?
Muss ich immer bei jeder Zahl in jeder Rechnung die grösser ist als int beinhalten kann (32'767) UL dahinter schreiben, damit es nicht standardmässig als int berechnet wird, sonder als unsigned long?
Also immer wenn ich irgendwo etwas rechne, mit Zahlen die grösser als 32'767 sind, muss ich z.B. UL dahinter anhängen, damit es richtig funktioniert?
Gilt das auch für dieses Beispiel:

unsigned long var1 = 123456UL + 123456UL;

?
Gibt es keine einfachere möglichkeit am Anfang alles als unsigned long zu deklarieren, z.B. eben durch

>unsigned long< var1 = 123456 + 123456;

(natürlich ohne die Pfeile), oder

unsigned long var1 = (unsigned long) 123456 + 123456;

?
Wäre nett wenn mir das kurz jemand erklären könnte.
Vielen Dank

Die Zahlen sind signed long, damit ist auch das Ergebnis signed long.

Das steht auch alles in jedem guten C++ Grundlagenbuch oder in dieser Referenz : Integer literal - cppreference.com

Vergiss die AI Gehirnprothesen.

Nein!

Zudem sind magische Zahlen böse.
Vermeidet man diese, verschwindet auch dein Problem.

nein.
nein.

ein Literal das schon größer ist als int, erzwingt nicht die Kennzeichnung.
Es geht darum dass wenn deine Berechnung größer werden kann als int, dass du dann mindestens einen Term benötigst der mind. so groß ist, wie das Ergebnis werden kann. Wenn du sonst keinen passend großen Term hast, kannst du das mit einem Literal machen.

void setup() {
  Serial.begin(115200);
  unsigned long var1 = 100;
  var1 += 86400;
  Serial.println(var1);

  unsigned long var2 = 123456 + 123456;
  Serial.println(var2);

  // Zusatz
  unsigned long var3 = 0;
  int var4 = 36000;
  var3 = var4 * 1000UL;  // 36000 * 1000 wäre größer als int
  Serial.println(var3);

}

void loop() {
  // put your main code here, to run repeatedly:

}

Schalte dir die ausführlichen Compiler Warnings ein, dann weist dich der Compiler meist auf derartige Probleme hin.

Ok, also dann wird der Arduino schon automatisch eine kleine Zahl als int benutzen und wenn sie zu gross wird als long oder später unsigned long?

Also dann sollten alle diese Beispiele stimmen:

unsigned long var1 = 123456 + 123456;
int var2 = 10;
var3 = var1 * var2;

var1 += 123456;

float var4 = 1.57;
int var5 = 10;
float var6 = 0;

var6 = var4 * var5;
var6 = var5 * var4;

?
Es wird mir leider nicht ersichtlich aus der von dir verlinkten Referenz...
Das mal zu combie, und ich sehe gerade das noiasca auch etwas dazu geschrieben hat, und da wird es irgendwie schon wieder unlogisch für mich...

Wie kann das sein:

  unsigned long var1 = 100;
  var1 += 86400;

  unsigned long var3 = 0;
  int var4 = 36000; // Wie kann int eine Zahl beinhalten die grösser als 32'767 ist?
  var3 = var4 * 1000UL;  // 36000 * 1000 wäre größer als int

Zur letzten Zeile mit var3, wieso muss ich dort in der Rechnung UL schreiben obwohl var3 schon als unsigned long deklariert wurde, und oben geht var1 += 86400 ohne UL? Genau das verstehen ich nicht, es erscheint mir unlogisch...

Oder anders gefragt (wenn die Variablen immer zuerst als unsigned long deklariert werden, ausser var4 als int), dann geht das:

  var1 += 86400;

geht das auch? :

  var1 = 86400 * 10;

und das geht auch, oder nicht? :

  var3 = 36000 * 1000;  // 36000 * 1000 wäre größer als int

das geht dann nicht mehr:

  var3 = var4 * 1000UL;  // 36000 * 1000 wäre größer als int

Zur Ergänzung, ich rede von einem normalen Arduino wie Uno oder (etwas speziellerem) Arduino pro mini 3.3V

weil var3 links von = steht
Es geht aber um die Terme auf der rechten Seite

du hast einen Uno, daher bietet es sich an, dass du das selber ausprobierst.
Bester Lerneffekt!

ja, weil, 86400 bereits zu groß fü ein int ( auf UNO ) ist.

nein, weil beide Elemente des Ausdrucks in ein int passen.
[Edit] Ups - 36000 passt auch bereits nicht in ein int (int ist ja vorzeichenbehaftet) - sollte also auch funktionieren.[/Edit]
Eigentlich ist es recht einfach: Die Berechnung auf der rechten Seite wird immer als int ausgeführt, solange alle einzelnen Elemente des Ausdrucks als int dargestellt werden können. Nur wenn bereits aus einem einzelnen Element ersichtlich ist, dass das größer als int ist, wir die Berechnung als long ausgeführt. Das kann entweder dadurch sein, dass der Zahlenwert bereits zu groß für ein int ist, durch ein angehangtes Suffix (z.B. UL) der Wert als long/unsigned long gekennzeichnet ist, oder dass der Ausdruck eine Variable enthält, die entsprechend deklariert wurde.
Der Typ der Variable, der das Ergebnis dann zugewiesen wird, ist unerheblich, denn das passiert erst nach der Berechnung.

Ausschlaggebend ist der verbaute µC, beim Uno R3 der ATmega328P, der auch beim Nano und ProMini verwendet wird. Daher entspricht der Typ int zweimal der ALU-Breite, also 2 * 8 = 16 Bit.

Die Spannung hat eventuell Einfluß auf den Takt, aber nicht auf die Berechnungen.

Kannst Du probieren:

Testprogramm
#include <Streaming.h>  // https://github.com/janelia-arduino/Streaming

void setup() {
  Serial.begin(115200);
  delay(500);
  Serial << "\nStart " << endl;

  unsigned long var1 = 123456 + 123456;
  int var2 = 10;
  unsigned long var3 = var1 * var2;
  Serial << "var1: " << var1 << "\tvar2: " << var2 << "\tvar3: " << var3 << endl;

  var1 += 123456;
  Serial << "var1: " << var1 << endl;

  float var4 = 1.57;
  int var5 = 10;
  float var6 = 0;
  Serial << "var4: " << var4 << "\tvar5: " << var5 << "\tvar6: " << var6 << endl;

  var6 = var4 * var5;
  var6 = var5 * var4;
  Serial << "var4: " << var4 << "\tvar5: " << var5 << "\tvar6: " << var6 << endl;

  int var7 = 1;
  int var8 = 2;
  float var9 = var7 / var8;
  var6 = 1.0 * var7 / var8;
  Serial << "var7: " << var7 << " \tvar8: " << var8 << " \tvar9: " << var9 << "\tvar6: " << var6 << endl;
}

void loop() {
  // put your main code here, to run repeatedly:
}
Anzeige
Start 
var1: 246912	var2: 10	var3: 2469120
var1: 370368
var4: 1.57	var5: 10	var6: 0.00
var4: 1.57	var5: 10	var6: 15.70
var7: 1 	var8: 2 	var9: 0.00	var6: 0.50

Schade.
Dann vielleicht doch ein Buch?

Bahnhof!
Nichts wird automatisch zu unsigned.

Ok. Vielen Dank allen für die Hilfe, ich glaube ich verstehe es jetzt in etwa.
Der zentrale Punkt um den sich alles dreht scheint das zu sein:

Bedeutet: Wenn eine Zahl in der Rechnung (rechts vom =) nicht in einen int passt, erkennt das der Compiler oder Arduino, und behandelt die Zahl automatisch als z.B. long wenn sie da hinein passt (und dann wird auch das Ergebnis als long behandelt). Aber das Ergebnis wird nicht vorher überprüft ob es in einen int passt, was dazu führen kann dass wenn 2 int Operanden als Ergebnis nicht mehr in einen int passen, es überläuft noch bevor es erkannt wird dass es zu klein ist.
Das erscheint mir wie ein Logik Fehler im Compilierer oder Arduino, aber irgendwie ist das halt so, ging wahrscheinlich nicht so einfach anders im Aufbau der ganzen Technik.

Somit sollte jetzt alles klar sein, und ich werde schauen ob das bei meinen Scripten alles aufgeht und ob der Compiler eine Fehlermeldung ausgiebt.

Vielen Dank für die Hilfe allen

... oder explizit als long gekennzeichnet ist :wink: - war in meinem von dir zitierten Ausschnitt nicht ganz klar beschrieben.

Ein Überlauf kann - speziell bei unsigned Werten - auch durchaus gewollt sein

Edit: Ja, du hast es weiter unten noch genauer erklärt, ich habe es weg gelassen weil ich meine Antwort kompakter halten wollte, weil schon ziemlich viel rund um diese Problem dass ich nicht verstanden habe geschrieben wurde:

Und ah ja, z.B. bei unsigned long kann der Überlauf gewollt sein... Dann macht es mehr Sinn, erscheint nur ziemlich verwirrend auf den 1. Blick...

Compiler werden von Menschen gemacht und die haben bestimmte Vorstellungen, was der können soll und was nicht.

In Basic hat a = 5 den Typ bestimmt, also der Typ rechts vom Gleichheitszeichen bestimmte den Typ der Variablen links davon. Ich habe den Eindruck, nach sowas suchst Du. Bei C muß man den Typ aber explizit angeben. Beim Umstieg kann es schonmal zu Konflikten kommen. Man muß sich halt auf die Ideen der Entwickler einlassen, geht nicht anders.

Wenn es Dich beruhigt, daß rechts vom Gleichheitszeichen unabhängig vom Typ der Variablen links davon gerechnet wird, habe ich zunächst auch nicht gewußt. Aber spätestens, wenn man auf die Nase fällt, kümmert man sich darum.

Nur noch mal erwähnen möchte ich, daß man zwischen syntaktischen Fehlern, die der Compiler erkennen kann, und Laufzeitfehlern unterscheiden muß.

Ein Beispiel ist der Überlauf nach etwas mehr als 49 Tagen von millis(). Bei Langläufern muß man das berücksichtigen, was aber kein Problem ist.

Ja das stimmt schon, es wird wahrscheinlich daran liegen, das ein Mensch eine Vorstellung hatte und dann halt mit den möglichen Mittel etwas kreiert hat.

Und ja das mit dem Überlauf bei millis(), der in einer bestimmten Rechnungsweise kein Problem macht, meinte ich auch.

Ein signed Überlauf führt in ein UB (undefind behavior).
Das ist in der Sprachdefnition so verankert.

Da das verboten ist, bist du es der den Bock schießt, und nicht der Kompiler.