Guten Abend,
ich führe in einer Konstanten per #define eine Berechnung durch:
#define MYVARIABLE 3500/81
Diese Konstante nutze ich, um in einer Funktion eine Berechnung
durchzuführen:
uint16_t result = 0;
result = 69*MYVARIABLE;
Das Ergebnis dieser Berechnung ist 65282, was FALSCH IST!
Richtig wäre 3500/81 * 69 = 2967.
Das korrekte Ergebnis erhalte ich, wenn ich folgendes tue:
uint16_t result = 0;
uint16_t myVar = MYVARIABLE;
result = 69*myVar;
In MYVARIABLE steckt die korrekte Zahl drin, doch bei der Multiplikation
läuft irgendwas falsch. Auch folgende Lösung klappt nicht:
uint16_t result = 0;
result = 69*(uint16_t)MYVARIABLE;
Das Ergebnis ist jetzt 554.
Wieso funktioniert die Berechnung result = 69*MYVARIABLE nicht richtig?
__
Ich lasse mir die Werte per UART anzeigen und habe einen Atmega2560.
Tja, du machst hier einige Fehler (neben der nicht richtigen
Formatierung)
1. 3500/81 = 43.2 -> da du nicht explizit castest, castet der Compiler
offensichtlich, also steht da 43. Das ist dein erstes Problem.
Sefco schrieb:> Richtig wäre 3500/81 * 69 = 2967.
Das schreibst du aber nicht hin!
Du schreibst
1
result=69*MYVARIABLE;
was ausgerollt
1
result=69*3500/81;
ist.
Und jetzt kommt dein engültiges Problem. 69*3500 passt nicht in eine
16bit Variable. Da das Ergebnis aber eine 16 Bit Variable ist, wird der
Compiler hier schon implizit casten und dir Stellen wegschmeissen.
Deswegen passt das Ergebnis auch überhaupt nicht zusammen.
Also für dich:
1. Formatierung benutzen!
2. KEINE IMPLIZITEN CASTS!
3. Das was du so programmiert hast, auch so hinschreiben!
4. Über die Größe von Variableninhalten nachdenken!
5. Versuchen vernünftig zu "debuggen", d.h. man rollt die Rechnung halt
dann mal per Hand auf und gibt sich Zwischenwerte aus.
6. Das Problem wäre auch aufm PC passiert, da kann man sowas auch
debuggen und testen.
Viele Dank für die Erklärung!
Mir war nicht klar, dass er zuerst 69*3500 bestimmt. Das ist mein
Problem. Das er mir 43 aus 43,... macht ist i.O. und mir auch klar.
Kannst du mir bitte noch erklären, was
1. Formatierung benutzen!
2. KEINE IMPLIZITEN CASTS!
bedeuten soll?
ist ein impliziter cast auf ne 16 Bit Variable (ist natürlich ein
einfaches Beispiel, bei dem das jeder sieht). Offensichtlicher für jeden
ist allerdings, wenn man explizit castet. Für das Makro ist es noch
offensichtlicher
1
#define MYVARIABLE (uint32_t)(3500/81)
2
3
wirdbeimeinsetzenzu
4
uint16_ta=MYVARIABLE*69;
5
6
<=>
7
8
uint16_ta=(uint32_t)(3500/81)*69;
Dann hat man zwischendurch nen cast auf ne 32 Bit Variable, dann wird
geteilt und dann wieder auf nen 16 Bit gecastet.
Sefco schrieb:> #define MYVARIABLE 3500/81
Die beiden schlimmsten Fehler sind:
a) den Ausdruck nicht geklammert: So
1
#define MYVARIABLE (3500/81)
Das sollte man immer tun bei Zahlenwerten, muss man bei Rechnungen.
Probier mal Spaßeshalber x=MYVARIABLE^2;
a) Warnmeldungen aus. Bei Deinem Konstrukt warnt der Compiler (oder wenn
der zu alt ist, LINT).
Achim S. schrieb:> a) den Ausdruck nicht geklammert: So
In diesem Fall ist das Ergebnis ohne Klammer genauer.
Bedenke: Erst Multiplizieren, dann Dividieren.
Es ist also insgesamt etwas "unglücklich" :-)
Der Cpp ist ein nicht-interaktiver Editor - so ähnlich wie m4 oder sed.
D.h. dass der Compiler etwas kompiliert, was wir nicht sehen. Das führt
dann bei Macros, die aussehen wie Funktionen, zu solchen Fehlern, wo
scheinbar auch Punkt- vor Strichrechnung verletzt wird.
Der Präprozessor gehört eigentlich auf den Müll der Geschichte.
Natürlich muss er aus Kompatibilitätsgründen erhalten bleiben. Aber
etwas anderes als #include (und allerhöchstens bedingte Compilation in
C) sollte er nicht mehr machen.
Konstanten sind const, in C++ evtl. als compile-time konstant dann
constexpr. In C++ hat man insgesamt mehr Möglichkeiten. Und in Zukunft
brauchen wir acuh kein #include in C++ mehr ...
Sefco schrieb:> #define MYVARIABLE 3500/81>> [...]>> In MYVARIABLE steckt die korrekte Zahl drin, doch bei der Multiplikation> läuft irgendwas falsch. Auch folgende Lösung klappt nicht:
***STOP***!
Hier liegt der Hund bereits im Pfeffer!
Bereits Deine Annahme in MYVARIABLE stecke eine Zahl drin ist falsch! Ja
sogar die Namensgebung lässt stark vermuten daß Du irrtümlicherweise
annimmst daß es überhaupt sowas wie eine Variable sei, das ist jedoch
falsch und führt Dich auf die falsche Fährte.
1
#define MYVARIABLE 3500/81
MYVARIABLE ist jetzt stellvertretend für die Zeichenfolge 3500/81 im
Quelltext und der Preprocessor macht nichts anderes als Textersetzung!
Und es ist NICHT sowas wie eine Variable, es ist ein Fetzen Quelltext
(text!) der einen bequemen Namen erhalten hat. Der Preprozessor wird den
text einsetzen und dann erst übergibt er es an den Compiler.
1
result=69*MYVARIABLE;
der Preprozessor macht daraus:
1
result=69*3500/81;
Und nann gibt er es an den Compiler.
Du verwendest einen Atmega, dort ist ein int 16 Bit breit. Per Standard
werden alle arithmetischen Ausdrücke vor der Rechnung auf int erweitert,
es sei denn einer davon wäre vorher bereits größer, ist hier aber nicht
der Fall, sowohl 69 als auch 3500 als auch 81 passen in ein int, also
wird die Rechnung von links nach rechts mit int ausgeführt und weil auf
dem AVR ein int nur 16 Bit hat läuft die Multiplikation über.
Du könntest aber schreiben:
1
#define MYVARIABLE 3500UL/81
dann wäre der Compiler gezwungen den ganzen Ausdruck in unsigned long
auszuwerten und erst am Schluss das Ergebnis wieder passend zum Typ von
result zu casten. Da das alles Konstanten sind wird die ganze Rechnung
bereits zur Compilezeit ausgeführt, ein Performancenachteil ergibt sich
dadurch also nicht.
Jedoch was bereits zuvor gesagt wurde: sei Dir bewußt daß da im ersten
Schritt erstmal nur eine reine Textersetzung stattfinden wird, wenn da
jetzt auch noch Addition und Multiplikation in unglücklicher Reihenfolge
vorkämen greift Punkt vor Strich und das Ergebnis wäre nicht was Du
erwartest.
Also mach um solche Ausdrücke lieber grundsätzlich Klammern drum:
1
#define MYVARIABLE (3500/81)
oder wenn Du den Rundungsfehler vermeiden willst lass die Klammern weg
und zwinge es auf long aber sei Dir der Tatsache bewußt in welcher
Reihenfolge das dann da steht und gerechnet wird nachdem der
Preprozessor alle Makros aufgelöst haben wird
Achim S. schrieb:> a) den Ausdruck nicht geklammert: So#define MYVARIABLE (3500/81)> Das sollte man immer tun bei Zahlenwerten, muss man bei Rechnungen.> Probier mal Spaßeshalber x=MYVARIABLE^2;
Das Beispiel ist natürlich nur begrenzt sinnvoll. ^ ist in C der XOR
Operator.
Wilhelm M. schrieb:> Der Präprozessor gehört eigentlich auf den Müll der Geschichte.
Nein. Aber ich würde mir wünschen daß er ein paar mehr Features bekommt,
evtl sogar Turing-vollständig wird. Das könnte im Gegenzug einige
Gehirnakrobatik-Würgarounds entschärfen wenn man die einfacher und
klarer hinschreiben könnte.
Wie gesagt: #include sollte in c++17 durch modules abgelöst werden -
wird nun leider erst c++20.
Bedingte Compilation ist eigentlich nur wegen den üblichen C-APIs
notwendig.
Und Macros braucht man in C++ eben nicht, da haben wir templates und das
ist Turing-vollständig. Deswwegen ist C++ das besser C, man muss ja kein
OOP mit C++ machen ...
Arduino F. schrieb:> In diesem Fall ist das Ergebnis ohne Klammer genauer.
Das kann aber hier kein Argument sein. Wenn er es genauer haben will,
kann er floatingpoint nehmen oder den Multiplikator ins Makro ziehen.
Wilhelm M. schrieb:> Und Macros braucht man in C++ eben nicht, da haben wir templates und das> ist Turing-vollständig. Deswwegen ist C++ das besser C, man muss ja kein> OOP mit C++ machen ...
Ich wollte mich eigentlich nicht triggern lassen, aber es hilft nix wenn
sowas ein Angemeldeter user sagt.
C++ ist nicht besser als C, da es alle (auch die ganzen beschissenen)
Konstrukte, übernimmt. Also ist es wenn dann eine Erweiterung von
beschissenen Konstrukten um noch mehr Konstrukte. Soviel dazu.
Bedingte Kompilation ist genau das, bedingte Kompilation. Und das ist
etwas wirklich tolles, wenn man Makros nicht für Berechnungen
missbraucht. Sondern eben für z.B. bedingte Kompilierung.
Beispiel: Festlegung von Arraygrößen am Headeranfang und nicht an
tausend stellen im Code. Als Beispiel. Oder verschiedene includes für
verschiedene Plattformen, bestes Beispiel ist doch dafür
ui schrieb:> Wilhelm M. schrieb:>> Und Macros braucht man in C++ eben nicht, da haben wir templates und das>> ist Turing-vollständig. Deswwegen ist C++ das besser C, man muss ja kein>> OOP mit C++ machen ...>> Ich wollte mich eigentlich nicht triggern lassen, aber es hilft nix wenn> sowas ein Angemeldeter user sagt.
C++ ist deswegen besser, weil es eine ganze Menge Merkmale hat (bei der
uC Entwicklung eben hauptsächlich templates und compile-time
expressions) die Arbeit ersparen und gleichzeitig viele Fehler bei der
Compilation auftreten lassen.
> C++ ist nicht besser als C, da es alle (auch die ganzen beschissenen)> Konstrukte, übernimmt. Also ist es wenn dann eine Erweiterung von> beschissenen Konstrukten um noch mehr Konstrukte.
Ja, und dazu gehört leider auch der Cpp.
> Soviel dazu.> Bedingte Kompilation ist genau das, bedingte Kompilation. Und das ist> etwas wirklich tolles, wenn man Makros nicht für Berechnungen> missbraucht. Sondern eben für z.B. bedingte Kompilierung.> Beispiel: Festlegung von Arraygrößen am Headeranfang und nicht an> tausend stellen im Code. Als Beispiel.
Dafür braucht es doch keinen Cpp, das ist jetzt aber ein ganz
unpassendes Beispiel!
> Oder verschiedene includes für> verschiedene Plattformen, bestes Beispiel ist doch dafür>
1
>#include<windows.h>
2
>
Wie gesagt: solange wir noch #includes haben, ja. (s.o.)
Wilhelm M. schrieb:> C++ ist deswegen besser, weil es eine ganze Menge Merkmale hat (bei der> uC Entwicklung eben hauptsächlich templates und compile-time> expressions) die Arbeit ersparen und gleichzeitig viele Fehler bei der> Compilation auftreten lassen.
Dazu muss ich leider sagen:
https://de.wikipedia.org/wiki/Embedded_C%2B%2B
Und mir sind inzwischen doch schon ein paar Plattformen übern Weg
gelaufen, die zwar prinzipiell C++ unterstüzten, aber eben z.B. keine
Templates. Fand ich auch schade, ist aber so.
ui schrieb:> Wilhelm M. schrieb:>> C++ ist deswegen besser, weil es eine ganze Menge Merkmale hat (bei der>> uC Entwicklung eben hauptsächlich templates und compile-time>> expressions) die Arbeit ersparen und gleichzeitig viele Fehler bei der>> Compilation auftreten lassen.>> Dazu muss ich leider sagen:> https://de.wikipedia.org/wiki/Embedded_C%2B%2B
Das ist doch fast 20 Jahre alt ...
Wilhelm M. schrieb:> Das ist doch fast 20 Jahre alt ...
Machts ja nicht besser, wenn es immer noch Compiler gibt, bei denen nur
sehr eingeschränkt C++ verfügbar ist. IMHO sind die besten Features von
C++
- templates
- Funktionsüberladung
ui schrieb:> Wilhelm M. schrieb:>> Das ist doch fast 20 Jahre alt ...>> Machts ja nicht besser, wenn es immer noch Compiler gibt, bei denen nur> sehr eingeschränkt C++ verfügbar ist. IMHO sind die besten Features von> C++> - templates> - Funktionsüberladung
Meine persönlichen Favoriten sind:
- variadic templates
- constexpr functions
- constexpr-if
- const-correctness
und viele Kleinigkeiten:
- fold expressions
- using template alias
- ranged-based for
Aber die Liste ist unvollständig!!! Das ist nur das, an was ich icm
Moment auf die Schnelle denke (und bezieht sich nur auf uC).
Meistens fällt das mir erst wieder auf, wenn ich gezwungen bin rein
prozedural in C zu programmieren ...
ui schrieb:> Machts ja nicht besser, wenn es immer noch Compiler gibt, bei denen nur> sehr eingeschränkt C++ verfügbar ist.
Naja...
Der AVR übliche gcc ....
Solltest evtl. mal schauen, was der alles kann....
Templates, kein Problem.
Arduino F. schrieb:> ui schrieb:>> Machts ja nicht besser, wenn es immer noch Compiler gibt, bei denen nur>> sehr eingeschränkt C++ verfügbar ist.>> Naja...> Der AVR übliche gcc ....> Solltest evtl. mal schauen, was der alles kann....> Templates, kein Problem.AVR ist ja nicht alles!
Es war wohl so gemeint, dass der g++ aber nicht alle Plattformen kann,
und clang++ auch nicht.
Wilhelm M. schrieb:> In C++ hat man insgesamt mehr Möglichkeiten.
Ja, das wissen wir.
Ist aber kein Grund, in jedem, aber auch wirklich jedem Thread, der
NIX mit C++ zu tun hat, immer wieder den gleichen Sermon runterzuleiern
und dein C++ hochzupreisen...
Wilhelm M. schrieb:> Wie gesagt: #include sollte in c++17 durch modules abgelöst werden
Hieße das, dass man nicht mehr mehrere Dateien, h und cpp aktuell halten
muss, sondern der Compiler sich selber raussucht, welche Klassen es
gibt? Das ist für mich einer der größten Vorteile von Java, weil es mich
stört, für jede Änderung extra nochmal die Headerdatei anpassen zu
müssen.
Dussel schrieb:> Hieße das, dass man nicht mehr mehrere Dateien, h und cpp aktuell halten> muss, sondern der Compiler sich selber raussucht, welche Klassen es> gibt?
Was bedeutet das? Oder welches Problem hast Du bzw. löst Du damit?
Johann L. schrieb:> Wilhelm M. schrieb:>> In C++ hat man insgesamt mehr Möglichkeiten.>> Ja, das wissen wir.>> Ist aber kein Grund, in jedem, aber auch wirklich jedem Thread, der> NIX mit C++ zu tun hat, immer wieder den gleichen Sermon runterzuleiern> und dein C++ hochzupreisen...
Es geht doch gar nicht um C oder(!) C++.
Solche Fehler wie der in diesem Thread treten leider immer wieder auf,
weil man ein altes Werkzeug (Cpp) benutzt in einer Art, die heute nicht
mehr notwendig ist. Leider kann man das Werkzeug nicht abschaffen. Aber
man kann einen eigenen Übergang zu besserem Code machen. Der hier
diskutierte C-Code lässt sich auch als C++-Code übersetzen (zumindest
der gezeigte Teilbereich, aber ich denke, dass das allgemein für die
überwiegende Menge gilt. Ich weiß auch, dass es Compiler-spezifische
Erweiterungen bzw. (noch) nicht standardisierte Erweiterungen gibt).
Dann kann man eben Dinge besser machen.
Hier konkret: statt #define verwendet man constexpr und
constexpr-functions (ggf. zusammen mit namespaces).
Achim schrieb:> Dussel schrieb:>> Hieße das, dass man nicht mehr mehrere Dateien, h und cpp aktuell halten>> muss, sondern der Compiler sich selber raussucht, welche Klassen es>> gibt?>> Was bedeutet das? Oder welches Problem hast Du bzw. löst Du damit?
Das möchte und kann ich nicht alles aufschreiben, das haben andere Leute
schon besser gemacht, und es gab ja einen Entwurf, der es leider nicht
in C++17 geschafft hat, der aber in clang/clang++ schon drin ist:
http://clang.llvm.org/docs/Modules.html
Viel Spaß beim Lesen!