Forum: Mikrocontroller und Digitale Elektronik Rechenfehler bei *


von Elf (Gast)


Lesenswert?

Guten Tag. Ich glaub jetzt brauch ich eure Hilfe.

Ich hab eine "komplizierte" Rechnung in Einzelschritte aufgeteilt, weil 
sie nicht funktioniert hatte.

[ich programmier mit AVR-Studio 6 und Atmega8]

Hier die ursprüngliche Zeile (tc ist uint32_t):

tc = 65536-(256*TimerCountOVF+TCNT0)/24;

Als erstes hab ich die Variablen durch Zahlen ersetzt die ich kenne:

tc = 65536-(256*1953+32)/24;

Weil das immer noch nicht funktioniert hat (ich lese das Ergebnis aus 
und es steht falsch drin) hab ich folgendes probiert: Ich hab die 
Rechnung in einzelschritte aufgeteilt und immer das Teilergebnis 
vorausgesetzt.

tc = 65536-20833; //funktioniert

usw... dann komm ich zu diesen Schritten

tc = 1953*256;
tc = tc+32;
tc = tc/24;
tc = 65536-tc;

Das funktioniert NICHT. Jetzt hätte ich gedacht, es liegt doch an einem 
Überlauf oder so - aber DAS funktioniert:

tc = 499968;
tc = tc+32;
tc = tc/24;
tc = 65536-tc;

Also schließe ich darauf, dass das mit dem * nicht klappt. Aber was???

Vielen Dank schonmal für eure Hilfe,

Beste Grüße,
Elf

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Probier mal an Stelle von
> tc = 1953*256;

das hier

  tc = 1953L * 256L;

aus.

Und überleg dann, was geschieht.

Tip: Wie groß kann ein int werden? Und welchen Datentyp haben 
numerische Konstanten?

von Stefan E. (sternst)


Lesenswert?

> (256*1953+32)

Wird in int gerechnet, und läuft über.

> 1953*256

Dito.

Elf schrieb:
> Hier die ursprüngliche Zeile (tc ist uint32_t):
>
> tc = 65536-(256*TimerCountOVF+TCNT0)/24;

Dass tc ein uint32_t ist, ist ziemlich irrelevant. Der Typ auf der 
linken Seite beeinflusst nämlich nicht, was auf der rechten Seite 
passiert.

von (prx) A. K. (prx)


Lesenswert?

In einer Zuweisung hat der Datentyp links keinen Einfluss auf die 
Rechnung rechts.

von J.-u. G. (juwe)


Lesenswert?

Dann kann ja auch gleich der passende Wettbewerbsartikel verlinkt 
werden:
http://www.mikrocontroller.net/articles/Plattformunabh%C3%A4ngige_Programmierung_in_C#Integer-Berechnungen

von Elf (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Und überleg dann, was geschieht.


Mhhh... vielleicht rechnet er dann in Long?
Okay, ich wusste nicht, dass der C-Compiler überlaufen kann. Aber jetzt 
weiß ich's :) Stimmt denn meine Annahme?

Werde es gleich probieren...

Vielen Dank euch allen.

von Dieter W. (dds5)


Lesenswert?

Der Compiler kann nicht überlaufen, aber dein Rechenergebnis.

tc = 1953*256

Int * Int = Int, nur das Ergebnis 499968 ist für Int zu groß - läuft 
also folglich über.

von Elf (Gast)


Lesenswert?

unglaublich. Das war tatsächlich der ganze Fehler und jetzt läuft's 
einwandfrei. Ihr seid die Besten xoxo

von Elf (Gast)


Lesenswert?

ist es dann so, dass

BPM = (60000000/(65536-cnt_pres))/24;

nur deshalb funktioniert, weil 600... automatisch schon ne Long ist und 
deshalb die ganze Rechenoperation in Long ausgeführt wird?

Gruß,
Elf

von J.-u. G. (juwe)


Lesenswert?

Elf schrieb:
> nur deshalb funktioniert, weil 600... automatisch schon ne Long ist und
> deshalb die ganze Rechenoperation in Long ausgeführt wird?

Ja. Willst Du Dir jetzt jeden Sachverhalt einzeln abgesegnen lassen?

von Elf (Gast)


Lesenswert?

J.-u. G. schrieb:

> Ja. Willst Du Dir jetzt jeden Sachverhalt einzeln abgesegnen lassen?

Brauchst du ne Antwort auf die Frage?
Falls ja: Nein
Wenn nicht: Ein "Ja" hätte auch gereicht. Trotzdem Danke.

PS: War das jetzt so genervt gemeint, wie ich es verstanden habe oder 
bin ich nur zu sensibel für dieses Forum?

Gruß,
Elf

von M. K. (sylaina)


Lesenswert?

Elf schrieb:
> PS: War das jetzt so genervt gemeint, wie ich es verstanden habe oder
> bin ich nur zu sensibel für dieses Forum?

Es war wahrscheinlich so gernervt gemeint wie du es verstanden hast aber 
das ist hier im Forum normal. Manch einer ist hier recht sensibel und 
reagiert dann so.

Deine Frage finde ich OK, denn das wird nur in wenigen Lehrbüchern 
wirklich erklärt. Diesen Stolperstein in C hab ich auch mitgenommen bei 
meinen ersten µC-Programier-Experimenten und stand erstmal mit fragendem 
Gesichtsausdruck übern Quellcode.

von Elf (Gast)


Lesenswert?

Michael Köhler schrieb:
> Deine Frage finde ich OK,

Vielen Dank für die aufmunternden Worte. Ich habe viel gelernt hier :)

Thema-Ende.

Gruß,
Elf

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

Michael Köhler schrieb:
> Diesen Stolperstein in C hab ich auch mitgenommen bei
> meinen ersten µC-Programier-Experimenten und stand erstmal mit fragendem
> Gesichtsausdruck übern Quellcode.

Ach, und in jeder anderen Hochsprache (neben C) oder gar in Assembler 
gibt es keine Datentypen und keinen Zahlenüberlauf?

von (prx) A. K. (prx)


Lesenswert?

Wegstaben Verbuchsler schrieb:
> Ach, und in jeder anderen Hochsprache (neben C) oder gar in Assembler
> gibt es keine Datentypen und keinen Zahlenüberlauf?

In PL/I ist das Produkt aus FIXED(5,2) und FIXED(7,1) vom Typ 
FIXED(13,3). Ein Überlauf kann in der Multiplikation innerhalb der 
Grenzen der Implementierung also nicht auftreten. Siehe 
http://publibfp.boulder.ibm.com/cgi-bin/bookmgr/BOOKS/IBM3L101/3.3.1.2?SHELF=ibmsh306&DT=19950506201638#TBLFIG16

Im aus Unix stammenden "bc" (Basic Calculator) gibt es tatsächlich keine 
Datentypen und die Grenze zum Überlauf liegt (fast) nur in den 
Ressourcen des verwendeten Rechners.

von hui (Gast)


Lesenswert?

A. K. schrieb:
> In PL/I ist das Produkt blablablablablabla

Dann programmier halt deinen Atmel in deiner tollen Sprache...

von Svenska (Gast)


Lesenswert?

> Ach, und in jeder anderen Hochsprache (neben C) oder gar in Assembler
> gibt es keine Datentypen und keinen Zahlenüberlauf?

Wie weit oben hätten's denn gerne? Der Datentyp Variant (Visual Basic) 
rüstet z.B. automatisch auf, wenn was nicht passt. Scriptsprachen 
genauso. Andere Programmiersprachen werfen Exceptions.

Je nachdem, aus welcher Richtung man kommt, ist so ein Überlaufverhalten 
gerade nicht offensichtlich.

> PS: War das jetzt so genervt gemeint, wie ich es verstanden habe oder
> bin ich nur zu sensibel für dieses Forum?

Deine Frage war gut, denn sie zeigte, dass du verstanden hast.

von M. K. (sylaina)


Lesenswert?

Wegstaben Verbuchsler schrieb:
> Ach, und in jeder anderen Hochsprache (neben C) oder gar in Assembler
> gibt es keine Datentypen und keinen Zahlenüberlauf?

Was hat das mit dem Problem zu tun? Der TE ist über den Stein 
gestolpert, dass der Datentyp links vom =-Zeichen nicht die Bereich 
rechts vom =-Zeichen bestimmt sondern lediglich durch die Größe der 
benutzten Konstanten bestimmt wird. Ich rede jetzt übrigens nur von 
Beispielen, die dem hier im Thread genannten entsprechen aber ich bin 
mir auch sicher, dass hier der ein und andere Volldepp rumrennt, der 
immer noch ein Lücke findet. Ein bischen mitdenken und überlegen, was 
der Gegenüber gemeint haben könnte ist hier im Forum leider allzuoft als 
Mangelware zu betrachten... :(

von Wegstabenverbuchsler (Gast)


Lesenswert?

Michael Köhler schrieb:
> er TE ist über den Stein
> gestolpert, dass der Datentyp links vom =-Zeichen nicht die Bereich
> rechts vom =-Zeichen bestimmt sondern lediglich durch die Größe der
> benutzten Konstanten bestimmt wird.

Genau, das ist weder C-spezifisch, sondern gibt es auch in anderen 
Hochsprachen, und insbesonder in Assembler ;-)

von c-hater (Gast)


Lesenswert?

Wegstabenverbuchsler schrieb:

> Genau, das ist weder C-spezifisch, sondern gibt es auch in anderen
> Hochsprachen, und insbesonder in Assembler ;-)

Blödsinn.

Jeder mir bekannte Assembler berechnet Integers zur Entwurfszeit einfach 
mit dem größten ihm bekannten Integer-Datentyp und meckert, wenn man 
so ein Entwurfszeit-Ergebnis irgendeiner Laufzeit-Entität zuweisen 
möchte, die es nicht fassen kann.

Das Verhalten von C, per Default zur Entwurfszeit nur mit 16Bit zu 
rechnen und bei Bereichsüberschreitung von numerischen Literalen noch 
nicht einmal zu warnen, war schon vor 40 Jahren idiotisch und ist es 
heute erst recht.

Das fällt sogar noch hinter die Fähigkeiten der aus heutiger Sicht 
steinzeitlichen Assembler zurück, zu denen C ursprünglich mal als 
Erweiterung in Form eines simplen Satzes primitiver Macros geschrieben 
wurde. Die Syntax von C hat sich seitdem zwar gar fürchterlich 
aufgebläht, aber die Benutzbarkeit der Sprache ist dadurch kein bissel 
besser geworden. Ganz im Gegenteil...

Das ist einfach unterirdisch. C halt. Eine Sprache für halbwissende 
Ignoranten. Vor 40 Jahren genauso wie heute.

Na gut, haben wir also letztlich doch noch einen Vorteil von C gefunden: 
Die Kontinuität... Jeder halbwissender Ignorant in all den Jahrzehnten 
fühlte sich natürlich toll, wenn er dem Nachwuchs auf vielen Seiten 
erklären konnte, wo überall die vielen Tücken dieser unlogischen 
Scheiß-Sprache lauern.

Auf die Idee, den ganzen merkwürdigen Quatsch einfach durch eine 
wirkliche SPRACHE zu ersetzen, ist eigenartigerweise nur eine Minderheit 
gekommen...

Ich folgere daraus, daß C-Programmierung ein erhebliches Potential hat, 
das menschliche Gehirn komplett auszubrennen. Ja, ich merke das auch 
jedesmal, wenn ich selber gezwungen bin, in C zu programmieren. Das ist 
wirklich überaus anstrengend und fehlerträchtig und ich danke jedesmal 
Gott, wenn wieder ein Projekt kommt, wo nicht C explizit gefordert wird 
und ich selber eine bezüglich des Ziels sinnvolle Sprache wählen kann 
(wobei das Ergebnis durchaus variabel, aber niemals C ist).

von Pe (Gast)


Lesenswert?

@c-hater:
...und was wäre dann eine "sinnvolle" Programmiersprache? Ich bin nicht 
auf C festgenagelt, Deinen beiden Posts nach gibt es bessere und weniger 
fehlerträchtige Umgebungen.

Ich denke, das ist eine Diskussion, die religionsartig vonstatten geht. 
Der eine schwört auf diese, der andere auf jene Sprache. Es gibt keinen 
Konsens, den alle akzeptieren.

Servus
Pe

von Karl H. (kbuchegg)


Lesenswert?

c-hater schrieb:

> Das fällt sogar noch hinter die Fähigkeiten der aus heutiger Sicht
> steinzeitlichen Assembler zurück, zu denen C ursprünglich mal als
> Erweiterung in Form eines simplen Satzes primitiver Macros geschrieben
> wurde. Die Syntax von C hat sich seitdem zwar gar fürchterlich
> aufgebläht,

Eigentlich ... nicht.

Syntaktisch hat sich in C seit den Zeiten eines K&R nicht wahnsinnig 
viel getan. Gerade das C-Gremium ist sehr konservativ, wenn es darum 
geht, Änderungen an der Sprache abzusegnen.

> Das ist einfach unterirdisch. C halt. Eine Sprache für halbwissende
> Ignoranten.

Nicht wirklich.
C ist halt keine Sprache für Leute, die eine Tante zum Lulu-gehen 
brauchen.
Gerade deshalb ist es wichtig, sie von der Pieke auf zu lernen.

Und wie sich die Datentypen in einer Berechnung bestimmen, sorry, aber 
das steht tatsächlich in so ziemlich jedem Lehrbuch, das sein Geld wert 
ist. Denn das ist ein wichtiger Punkt in der Expression Evaluation.

> Ich folgere daraus, daß C-Programmierung ein erhebliches Potential
> hat, das menschliche Gehirn komplett auszubrennen.

Deine Folgerung ist falsch. Die Folgerung sie du ziehen solltest ist, 
dass man C nicht durch Zusammenfragen von anlassbezogenem Achtelwissen 
in Foren lernen kann, sondern sich von ordentlicher Literatur durch das 
Thema führen lassen sollte. Selbst dann bleiben noch Detailfragen offen, 
aber nicht mehr auf dem Level von "Wie bestimmen sich eigentlich die in 
Ausdrücken benutzten Datentypen und wie beeinflussen diese die Art und 
Weise, wie der Compiler die Operation in Code umsetzt".

So wie du das weiter oben ja auch schon korrekterweise angedeutet hast.


> Das Verhalten von C, per Default zur Entwurfszeit nur mit 16Bit
> zu rechnen

mit int, aber ... geschenkt

> und bei Bereichsüberschreitung von numerischen Literalen noch
> nicht einmal zu warnen

Dein Compilerbauer kann Warnungen einbauen soviele er will. Der 
C-Standard schreibt lediglich vor, welche Fehler gemeldet werden müssen. 
Aber Warnungen kann jeder machen wie er will.

>, war schon vor 40 Jahren idiotisch und ist es
> heute erst recht.

Ganz im Gegenteil. Es soll keinen Unterschied zwischen
1
  int j = 40000;
2
  int k = 40000;
3
4
  long l = j * k;

und
1
  long l = 40000 * 40000;

geben. Denn beides sind dieselben Operationen. Ob da jetzt Variablen 
involviert sind (und die 40000 auf irgendwelchen anderen Wegen 
entstehen, die für den Compiler nicht einsichtig sind), darf keinen 
Unterschied im Ergebnis machen. Also muss der Compiler genau das tun, 
wenn er den Ausdruck durch das Constant Folding vereinfacht, was auch 
passieren würde, wenn der Ausdruck zur Laufzeit berechnet werden würde. 
Wenn er nett ist, dann wirft er eine Warnung (wozu er nicht verpflichtet 
ist). Aber als Ergebnis muss Bit für Bit dasselbe rauskommen (inklusive 
Overflow) - dazu ist er durch die 'As-If' Regel verpflichtet. Und in 
früheren Jahren war das gar nicht so einfach, weil der Compiler zb für 
Floating Point Berechnungen dann auch zb das Verhalten der FPU der 
Zielhardware nachbilden musste.

Du magst C nicht. Das ist dein gutes Recht. Aber hör bitte auf mit 
deinem Halbwissen über C über die Sprache zu schimpfen. C ist sicher 
keine einfache Sprache und es gibt viele Details, die man wissen muss 
und ja, einige Details sind nicht so prickelnd gelöst. Aber all das kann 
man lernen und wenn man erst mal ein paar grundlegende Basisprinzipien 
verstanden hat, die wesentliche Grundlagen der Sprache bilden, dann 
versteht man bei manchen Dingen um einiges besser, warum die Dinge genau 
so sind wie sie sind. Viele empfinden C gerade im µC-Bereich als einen 
brauchbaren Mittelweg zwischen der Befreiung von den ebenfalls vielen 
kleinen Details, die man in der Assembler-Programmierung kennen und 
können und beherzigen muss und dem Overhead, dem man sich durch die 
Verwendung einer Hochsprache mit möglichst guter Fehlererkennung (auch 
zur Laufzeit) aufzwirbelt. Und in diesem Marktsegment macht C gar keine 
so schlechte Figur.

von c-hater (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Ob da jetzt Variablen
> involviert sind (und die 40000 auf irgendwelchen anderen Wegen
> entstehen, die für den Compiler nicht einsichtig sind)

Das ist doch Unsinn. Natürlich weiß der Compiler immer, ob eine 
Operation zur Entwurfszeit (eigentlich: Compilezeit) geschieht oder zur 
Laufzeit. Dementsprechend könnte er auch problemlos sinnvoll warnen, 
wenn eine Warnung nötig ist, nämlich genau dann, wenn irgendein 
Entwurfszeit-Resultat einer Laufzeit-Entität zugewiesen wird, die es 
nicht fassen kann. Jeder primitive Assembler beherrscht das und 
natürlich könnten moderne C-Compiler das problemlos ganz genauso 
leisten.

> darf keinen
> Unterschied im Ergebnis machen.

Das ist doch nur ein C-Axiom, keine sachlich begündete Notwendigkeit 
oder gar etwas Wünschenswertes. Und es ist darüber hinaus ein sinnloses 
und fehlerträchtiges Axiom, welches dem Programmierer Arbeit aufbürdet, 
die die Maschine viel besser und zuverlässiger selber erledigen könnte.

Allein die Existenz dieses gesamten Threads beweist das doch schon 
absolut zweifelsfrei, oder nicht? Der Auslöser war ja wohl 
offensichtlich der fünfundzwanzigmillionste Möchtegern-C-Programmierer, 
der sich damit in's Knie gefickt hat.


Wenn man sich übrigens mal näher mit den Ursprüngen von C beschäftigt 
hat, dann weiß man auch, wie dieser Schmutz überhaupt in die Sprache 
gelangt ist. Denn de facto war die Unmöglichkeit, numerische Overflows 
des Präprozessors im zugrunde liegenden Assembler abzufragen (der damals 
auch zur Entwurfszeit nur mit 16 Bit signed gerechnet hat), der 
eigentliche Grund für dieses ziemlich sinnlose und fehlerträchtige 
Verhalten des C-Aufsatzes. Und nur, weil dies vor 40 Jahren mal ein 
Problem war, schleppt C den Scheiß bis heute mit. Das ist zwar völlig 
idiotisch, aber eben Kontinuität.

Manche Leute versteigen sich sogar soweit, das als 
"Abwärtskompatibilität" zu bezeichnen und als etwas positives zu sehen. 
Nicht zuletzt die Hüter des C-Standards...

Das einzige, was ich zur Entlastung von C anführen kann, ist: Es gibt 
etliche andere Sprachen, die ähnlich idiotische Altlasten mit sich 
herumschleppen...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

c-hater schrieb:
> Blödsinn.

Tja, warum wohl ist heute nicht Ada und auch nicht Assembler die
dominierende Sprache in der Controllerprogrammierung?  Offenbar sind
die Eigenwilligkeiten der Sprache C bei weitem nicht das Argument, das
du uns hier gern weiß machen möchtest.  Mit denen kann man sich
arrangieren, wenn man das einmal gelernt hat, der Aufwand für das
Lernen ist einmalig.  Der exorbitante Aufwand, den man allein für die
logische Fehlerfreiheit eines größeren Assemblerprogramms treiben muss
(und zwar sowohl bei der Erstellung als auch bei der Pflege) schließt
in fast allen praktisch relevanten Bereichen, in denen man mit der
Software Geld verdienen muss, die Nutzung von Assembler als erste Wahl
einfach mal aus.  Das kann und will keiner bezahlen, vor allem nicht
$KUNDE. ;-)

Ada wäre sicher interessant, aber ernsthaft durchgesetzt hat es sich
außerhalb einiger Nischen halt doch nicht.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.