Hallo,
folgendes lässt sich auf einem 8Bit AVR nicht kompilieren.
1
uint64_tvar{18'446'744'073'709'551'615};
error: integer constant is so large that it is unsigned [-Werror]
Habe verschiedene avr-gcc Toolchains ausprobiert. Kürze ich den Wert um
eine Stelle klappt es. Kennt jemand den Grund für den gekürzten
Wertebereich?
Es funktioniert mit beiden Varianten. Danke Euch.
Mich wundert das jetzt nur ein wenig. Ich war bis jetzt der Meinung das
man Präfixe wie 'UL' nur beim rechnen mit direkt eingesetzten Literalen
benötigt. Weil sonst in Standard 'int' gerechnet wird. Hier steht
allerdings der Datentyp mit uint64_t fest.
Hier ist 'var' auch 32 Bit ohne zusätzliches Präfix.
1
uint32_tvar{1};
Grob probiert, dass Problem taucht ab ca. 9E+18 auf.
Wenn ich mir das genauer anschaue liegt das auf der 63Bit Grenze.
1
int64_tvar1{9'223'372'036'854'775'807};// ok
2
uint64_tvar2{9'223'372'036'854'775'807};// ok
3
uint64_tvar3{9'223'372'036'854'775'808};// nicht ok, zu groß
Mit größer dem halben Wertebereich gibts ein Problem. Ist das normales
Verhalten? Muss man sich das einfach merken das uint64_t eine
Sonderbehandlung benötigt, also als Gegeben hinnehmen oder gibts dafür
erklärbare Gründe? Weil die Sonderbehandlung ist ab 19 Digits notwendig.
Hallo,
okay "Suffix". ;-)
signed (bzw. default int) ist ein Literal wenn man es ohne Datentyp
verwendet.
Das hier rechnet ohne Suffix wie erwartet richtig.
1
uint32_ta{4'000'000'000};
2
uint32_tb{1};
3
uint32_tc=a-b;
Hierfür benötigt man ein Suffix, weil sonst alles als int behandelt
wird, bevor das Ergebnis c zugewiesen wird.
Veit D. schrieb:> Hier ist 'var' auch 32 Bit ohne zusätzliches Präfix.> uint32_t var {1};
Hier wird eine Konstante (1) vom Typ int zur Initialisierung von var
verwendet; ein positiver int Wert kann problemlos nach uint32_t
konvertiert werden.
Veit D. schrieb:> Wenn ich mir das genauer anschaue liegt das auf der 63Bit Grenze.
Klar, weil sobald diese Grenze überschritten wird, wird der Wert der
Konstanten zu groß für den Wertebereich von long long int, dem größten
vorzeichenbehafteten Ganzzahlen-Typ, weshalb der Compiler mit dieser
Ziffernfolge nichts mehr anfangen kann.
Veit D. schrieb:> Hier steht allerdings der Datentyp mit uint64_t fest.
Für den Typ spielt es keine Rolle, was du nachher damit machst. Das
uint64_t deiner Variable ist für den Typ des Literals nicht von
Bedeutung.
Veit D. schrieb:> Hier ist 'var' auch 32 Bit ohne zusätzliches Präfix.> uint32_t var {1};
var ja, aber 1 nicht. 1 ist vom Typ int. Nun passt 1 in den Wertebereich
von int, daher gibt es hier kein Problem. Der größtmögliche Typ für ein
Integer-Literal ohne Suffix ist long long, und da passt
9'223'372'036'854'775'808 nicht rein. In beiden Fällen hat das mit dem
Typ der Variablen gar nichts zu tun.
Hallo,
okay, habs wohl fast verstanden oder doch noch nicht.
Das heißt am funktionierenden Bsp. ohne Suffix uint32_t var
{4'000'000'000} wird das erst nach long long konvertiert, weil es in ein
long nicht reinpasst und danach nochmal nach unsigned long konvertiert?
Komischerweise kann er hier jedenfalls nach unsigned ohne Suffix
konvertieren. Oder anders gefragt, in welcher Wandlungsreihenfolge wird
hier 4'000'000'000 nach uint32_t konvertiert? Weil wenn er es hier
gleich von int nach uint32_t konvertiert, dann könnte er auch gleich von
int nach uint64_t konvertieren. Versteht jemand mein Gedankenproblem?
@ Johannes S.
'ULL' wurde gleich zu Anfang mit getestet und funktioniert.
Johannes S. schrieb:> uint64_t var {18'446'744'073'709'551'615ULL};> printf("Hello World %lu", var);
Bist du dir sicher, dass %lu zu uint64_t passt?
Nicht nur auf deinem System.
Dirk B. schrieb:> Bist du dir sicher, dass %lu zu uint64_t passt?
ich bin sicher das es auf 'meinen' Cortex-M nicht passt, da möchte der
gcc ein %llu haben. Ich weiß auch das es dafür Macros gibt die das
systemabhängig richtig machen, aber die machen das deutlich
unleserlicher und die verwende ich nicht gerne.
Es ging aber auch nicht um das printf, das war nur Q&D um Test. Und im
Online GDB gehts eben mit %lu.
Veit D. schrieb:> ohne Suffix uint32_t var> {4'000'000'000} wird das erst nach long long konvertiert,
Nein, da wird nichts "erst" konvertiert zumindest nicht, wenn du einen
impliziten Cast meinst. Es ist der Parser/Compiler, welcher dort das
Literal in 64 Bit erzeugt.
Erst die Initialisierung erfordert den implizite Cast nach unsigned long
Tipp:
Beschäftige dich mit TypeTraits.
using Type = decltype(4'000'000'000);
Dann kann man damit den Type ausführlich untersuchen.
PS:
TypeTraits werden beim AVR Gcc nicht mitgeliefert.
Gibt aber alternative Implementierungen.
Veit D. schrieb:> Versteht jemand mein Gedankenproblem?
Nein, ich nicht.
Nicht das Problem, was du mit den Typen hast.
Aber das, welches bei dir die Erkenntnis behindert, das glaube ich zu
kennen. Natürlich aus eigener Erfahrung.
Veit D. schrieb:> Das heißt am funktionierenden Bsp. ohne Suffix uint32_t var> {4'000'000'000} wird das erst nach long long konvertiert,
Nein, es wird nicht nach long long konvertiert, es ist bereits vom Typ
long long. Der Grund dafür ist, dass
> es in ein long nicht reinpasst
Sonst wäre es vom Typ long oder int.
> und danach nochmal nach unsigned long konvertiert?
Es wird konvertiert, aber nicht "nochmal", da das die erste und einzige
Konvertierung ist, die hier stattfindet.
> Komischerweise kann er hier jedenfalls nach unsigned ohne Suffix> konvertieren.
Signed kann immer nach unsigned konvertiert werden, nicht nur hier.
Dein ursprüngliches Problem hat mit Konvertierungen überhaupt nichts zu
tun, sondern damit, dass das Signed-Literal 18'446'744'073'709'551'615
außerhalb des vom GCC unterstützten Wertebereichs (-9223372036854775808
bis 9223372036854775807) liegt. Mit dem U-Suffix wird daraus ein
Unsigned-Literal, dessen Wertebereich von 0 bis 18446744073709551615
reicht.
Hallo,
ich glaube wir kommen der Sache näher. Ich dachte bis gestern ich hätte
das mit impliziten cast und wann ein Suffix notwendig ist verstanden.
Aber hier mit dem höchsten Wertebereich komme ich nicht klar warum sich
der Compiler dabei blöd anstellt.
Wenn hier
1
uint16_tvar{65'000}
2
uint32_tvar{4'000'000'000}
automatisch der Richtige implizite cast gemacht wird.
Warum dann nicht auch hier ?
1
uint64_tvar{18'446'744'073'709'551'615}
Ich meine der Compiler weiß doch das der Wert nicht in ein signed passt.
Genauso wie er es bei 4 Milliarden auch weiß. Hier macht er dann
demzufolge den richtigen impliziten cast nach uint32_t. Wenn der gcc
uint64_t nicht kennen würde könnte er ja mit dem Suffix ULL nichts
anfangen.
Aktuell würde ich mir merken das man auch bei einer
Variableninitialisierung mit Wertzuweisung größer int64_t ein Suffix
benötigt. Nur das warum ist trotz aller euren Erklärungen nicht zu 100%
klar.
Veit D. schrieb:> der Compiler dabei blöd anstellt
Es ist nicht der Compiler, welcher sich blöd anstellt.
Es ist die Sprachdefinition, welche das nicht hergibt.
Dein 18'446'744'073'709'551'615 passt einfach nicht in int64_t rein.
Und darum hauts dir das um die Ohren.
Du projizierst deine Wünsche auf den Compiler.
Da ist der Haken.
Er ist kein "Wünsch dir was"
Zu den Casts:
Mittlerweile warnen die Compiler bei (fast) jeder Verlust behafteten
impliziten Konvertierung. Das ist eine Gnade der Compiler(ersteller),
keine Pflicht.
Genau so ist es keine Pflicht, dass du die Sprachdefinition akzeptierst.
Aber, es ist ein Kampf gegen Windmühlen, wenn du dagegen anstrampelst.
Der Compiler bietet dir die Möglichkeit an jedes Literal, welches du
gerne als unsigned haben möchtest, auch ein U dran zu hängen.
Hallo,
wenn man die Aussage so allein betrachtet ist alles okay.
Wegen der Overflow Warnung. Deswegen schreibe ich die geschweiften
Klammern.
Nur warum funktioniert dann hierbei
1
uint32_tvar{4'000'000'000}
der implizite cast? Warum ist hier keine Suffix Unterstützung notwendig?
Das sollte eigentlich laut den Erklärungen genauso wenig funktionieren.
Tut es aber.
Wenn alle Literale oberhalb von 'int' ein Suffix benötigen würden wäre
es mir verständlicher, weil int der Standarddatentyp ist. Aber nun gibts
dummerweise ein funktionierendes uin32_t Bsp. was die Logik
durcheinander bringt. Das bringt mich außer Tritt.
Vergiss erst einmal sämtliche Casts und implizite Konvertierungen und
betrachte stattsessen das folgende, auf das Wesentliche reduzierte
Beispiel:
1
intmain(){
2
18'446'744'073'709'551'615;
3
}
Auch hier erhältst du die Warnung "integer constant is so large that it
is unsigned".
Nun schau in der folgende Tabelle nach, welche Datentypen für ein
Integer-Literal in Dezimalschreibweise in Frage kommen:
https://en.cppreference.com/w/cpp/language/integer_literal#The_type_of_the_literal
Es sind dies
- int
- long int
- long long int
In welchen dieser Datentypen passt die große Zahl?
Richtig: In keinen davon. Deswegen ist die riesige Zahl ohne einen
U-Suffix achlicht und ergreifend ein Fehler.
Mit einem U-Suffix sind die in Frage kommenden Datentypen lt. der
verlinkten Tabelle
- unsigned int
- unsigned long int
- unsigned long long int
Der letzte davon ist ausreichend groß, um die Zahl aufzunehmen, deswegen
gibt es hier keine Warnung.
Machst du die Zahl aber noch größer, so erhältst du erneut eine Warnung,
nämlich "integer constant is too large for its type".
Veit D. schrieb:> der implizite cast? Warum ist hier keine Suffix Unterstützung notwendig?> Das sollte eigentlich laut den Erklärungen genauso wenig funktionieren.> Tut es aber
Du irrst!
{4'000'000'000} wird vom Compiler zu einem long long als int64_t.
Der Wert lässt sich verlustfrei nach uint32_t konvertieren.
Also auch keine Warnung.
Warum auch?
Ist doch alles gut.
Cyblord -. schrieb:> Man kann auf das Suffix verzichten wenn es einen größeren Datentyp gibt,> welcher die Zahl als signed aufnehmen kann.
Nicht ganz korrekt...
Besser:
Ohne Präfix und ohne Suffix ist das numerische Literal immer signed.
Denn mit 0x Präfix gilt die Einschränkung nicht.
EAF schrieb:> Cyblord -. schrieb:>> Man kann auf das Suffix verzichten wenn es einen größeren Datentyp gibt,>> welcher die Zahl als signed aufnehmen kann.> Nicht ganz korrekt...>> Besser:> Ohne Präfix und ohne Suffix ist das numerische Literal immer signed.
Nur erklärt dies das Problem noch nicht. Das Problem ist dass es keinen
Datentyp gibt, welcher diese signed Zahl aufnehmen kann. Genau dieses
Detail fehlt hier einigen zum Verständnis. Dass die Zahl ohne Suffix
(und ohne Präfix) als signed interpretiert wird, scheint allgemein klar
zu sein.
Cyblord -. schrieb:> scheint allgemein klar zu sein.
Naja...
Dann müssten wir nicht darüber reden... nehme ich mal an.
Cyblord -. schrieb:> interpretiert
Das ist vielleicht das richtige Wort!
Der Compiler interpretiert den Quellcode.
Und erst danach, bei der Initialisierung der Variablen erfolgt der
implizite Cast.
Das sind 2 völlig unterschiedliche Vorgänge, die nix miteinander gemein
haben, außer, dass sie hier zufällig in eine Zeile stehen.
Für beide Vorgänge stehen die Regeln, nach denen das geschieht, im
Handbuch.
EAF schrieb:> Dann müssten wir nicht darüber reden... nehme ich mal an.
Nochmal:
Den meisten war klar dass die Typen ohne Suffix signed sind.
Denen war aber nicht klar, dass die Probleme daher kamen dass kein
nächstgrößerer Datentyp mehr für diesen signed Wert verfügbar war.
So interpretiere ich jedenfalls die Posts von Veit D.
Johannes S. schrieb:> Dirk B. schrieb:>> Bist du dir sicher, dass %lu zu uint64_t passt?>> ich bin sicher das es auf 'meinen' Cortex-M nicht passt, da möchte der> gcc ein %llu haben. Ich weiß auch das es dafür Macros gibt die das> systemabhängig richtig machen, aber die machen das deutlich> unleserlicher und die verwende ich nicht gerne.
Die sind aber die einzige Möglichkeit, das universell richtig zu machen.
Veit D. schrieb:> ich glaube wir kommen der Sache näher. Ich dachte bis gestern ich hätte> das mit impliziten cast und wann ein Suffix notwendig ist verstanden.
Es gibt keine impliziten Casts, sondern nur implizite Konvertierungen.
> Aber hier mit dem höchsten Wertebereich komme ich nicht klar warum sich> der Compiler dabei blöd anstellt.
Integer-Literals, die in dezimal und ohne Suffix angegeben sind, können
einen von genau drei Typen annehmen. Default ist int. Wenn das zu klein
ist, wird long genommen, und wenn das auch zu klein ist, wird long long
genommen. Und wenn das zu klein ist, gibt es einen Fehler. Alle anderen
Typen sind dafür verboten.
> Wenn hier> uint16_t var {65'000}> uint32_t var {4'000'000'000}> automatisch der Richtige implizite cast gemacht wird.
65'000 ist auf einem AVR vom Typ long, 4'000'000'000 vom Typ long long.
Mit der Konvertierung nach uint16_t bzw. uint32_t hat das immer noch
überhaupt nichts zu tun. Nochmal: Für den Typ des literal spielt der Typ
der Variablen, die damit in diesem Fall initialisiert wird, absolut
keine Rolle.
> Warum dann nicht auch hier ?uint64_t var {18'446'744'073'709'551'615}
Weil 18'446'744'073'709'551'615 schon falsch ist. Es ist zu groß für
long long, also kommt ein Fehler. Auch hier hat das rein gar nichts
damit zu tun, dass das dann in eine Variable vom Typ uint64_t
geschrieben wird.
> Ich meine der Compiler weiß doch das der Wert nicht in ein signed passt.
Er hat aber vom Standard die Vorgabe, dass es signed sein muss.
> Genauso wie er es bei 4 Milliarden auch weiß.
Hier reicht aber der Wertebereich von long long aus, um die Zahl
darzustellen.
> Hier macht er dann demzufolge den richtigen impliziten cast nach> uint32_t.
Das kommt nach der Stelle mit dem Problem.
> Aktuell würde ich mir merken das man auch bei einer> Variableninitialisierung mit Wertzuweisung größer int64_t ein Suffix> benötigt.
… größer long long.
Cyblord -. schrieb:> Denen war aber nicht klar,
Wenn ich den "Problemkomplex" analysiere, dann komme ich zu folgendem
Schluss:
Bisher wurden Variablen schon fleißig mit numerischen Literalen
initialisiert.
Eine Gewohnheit geworden.
Ist auch ok, völlig normal.
Plötzlich ein ungewohnter Wert, und eine Warnung.
Der Fehler war, nicht schon vorher mal ins Buch zu schauen, nach dem
Motto "Was passiert da wirklich?", sondern sich lieber in der Fantasie
einen Vorgang ausmahlen.
Der konkrete Fall macht dann die Diskrepanz zwischen Fantasie und
Realität zum Brennpunkt.
Natürlich wird dann erstmal überall der Übeltäter gesucht. Die eigenen
Vorstellungen/Fantasien, werden eigentlich nicht so gerne auf den
Prüfstand gestellt oder gar als irrig anerkannt. Sie müssten ja
korrigiert werden.
Motto:
> irren ist menschlich> im Irrtum verharren ist Dummheit
Das eigene Beharrungsvermögen steht da der Einsicht gerne quer im Weg
rum.
Eigentlich wäre es klüger, sich vor der ersten Verwendung von Literalen
kundig zu machen, so erst gar nicht diesen Irrweg beschreiten.
Aber, naja, wer ist schon immer uneingeschränkt klug?
Hallo,
ich denke ich habs verstanden. Ich danke Euch allen. Hier kamen
verschiedene Dinge, auch Gewohnheiten, zusammen die am Ende zu einem
großen Fragezeichen führten. ;-) Danke.