Hi,
wir sitzen gerade im Info Tutorium, und keiner (auch nicht die Tutoren)
kann sich erklären, warum bei einer Division 0 Rauskommt.
Codefetzen:
double a;
a = 4/8;
cout << a;
dann kommt a = 0 heraus.
gleiches gilt für float.
Woran liegt das?
Danke
Der Compiler ist leider nur begrenzt intelligent. 4 und 8 betrachtet er
als Integer-Zahlen, auch wenn die Rechnung für einen Menschen ganz
offensichtlich mit Gleitkommazahlen durchgeführt werden soll. Deshalb
muss man es dem Compiler explizit mitteilen, zum Beispiel so:
double a;
a = 4/8.0;
Was sind denn das für Tutoren, wenn sie diesen 08/15 Anfängerfehler
nicht in weniger als 5 Sekunden korrekt diagnostizieren?
Mark mag das scherzhaft gemeint haben, aber mit der Intelligenz des
Compilers hat das Ganze nichts zu tun, sondern mit den C++ Regeln.
Ich fände es tatsächlich sinnvoller, wenn der Compiler diejenige
Rechnung als "default" annehmen würde, die das genauere Ergebnis bringt
bzw. die zum Ergebnistyp passt. Wenn ein Mensch lesen kann dass links
von der Zuweisung eine Gleitkommavariable steht, dann kann ein Compiler
das auch ;-)
Mark Brandis schrieb:> Ich fände es tatsächlich sinnvoller, wenn der Compiler diejenige> Rechnung als "default" annehmen würde, die das genauere Ergebnis bringt> bzw. die zum Ergebnistyp passt. Wenn ein Mensch lesen kann dass links> von der Zuweisung eine Gleitkommavariable steht, dann kann ein Compiler> das auch ;-)
Mark: Es gibt Regeln nach denen der Compiler vorgeht! Diese Regeln muss
man nur kennen, um zu entscheiden, wie genau denn jetzt
double Farenheit = 80;
double Celsius;
Celsius = 5 / 9 * Farenheit + 32;
gerechnet wird. (Und damit haben wir dann hier auch das
Standardbeispiel, mit dem jeder C bzw. C++ Neuling in die Thematik "Wie
entscheidet der Compiler anhand der verwendeten Datentypen, wie eine
Berechnung durchzuführen ist")
Ansonsten könnte man nämlich gleich sagen: Alles wird als
Gleitkommadivision gemacht. Was dann zb in Fällen wie
Stunden = Minuten / 60;
Minuten = Minuten % 60;
absolut nicht gewünscht ist.
Mag sein. Zumindest eine Warnung könnte der Compiler aber durchaus
ausgeben, dass der Variablentyp auf der rechten Seite der Zuweisung vom
Variablentyp auf der linken Seite der Zuweisung abweicht. Das bei der
Übersetzung festzustellen kann doch so schwer nicht sein.
Und ja, ich bin als Anfänger auch genau darüber gestolpert :)
Mark Brandis schrieb:> Mag sein. Zumindest eine Warnung könnte der Compiler aber durchaus> ausgeben, dass der Variablentyp auf der rechten Seite der Zuweisung vom> Variablentyp auf der linken Seite der Zuweisung abweicht.
Dann bist du mit C bzw C++ bei der falschen Sprache.
Wer bei den einfachen Dingen, und ja das ist ist wirklich einfach, schon
eine LuLu Tante braucht, sollte einen großen Bogen um C++ bzw C machen.
Ich bin froh, dass er hier nicht warnt.
PS: Was hilft dir die Warnung im Farenheit-Celsius Beispiel? Der
Datentyp des Ausdrucks auf der rechten Seite ist double. Also würde
hier keine Warnung kommen!
Warnungen haben nur dann Sinn, wenn sie tatsächlich alle überhaupt
möglichen Fälle abecken können. Eine Warnung die nur in 10% aller
praktisch vorkommenden Fälle auftaucht hat nur einen Effekt: noch mehr
Fehler, weil sich insbesondere die Neulinge auf das Auftreten einer
Warnung verlassen. Und der Fortgeschrittene braucht die Warnung sowieso
nicht mehr.
> Und ja, ich bin als Anfänger auch genau darüber gestolpert :)
Natürlich. Jeder stolpert da in seiner ersten Programmiersprache drüber.
Ist mir auch nicht anders ergangen. Dazu hat man ja Tutoren, die diese
Klassiker kennen.
Und die Grundregel ist ziemlich einfach:
Wenn der Compiler entscheidet, wie eine Berechnung durchzuführen ist,
zählen einzig und alleine die Datentypen der beteiligten Operanden und
nicht was mit dem Ergebnis weiter passiert.
Diese Regel gilt nämlich immer und überall, wohingegen es bei
F = 8*a + (5 / ( 2 + b )) * 8;
gar nicht mehr so einfach detektierbar ist, ob die Division nun als
Gleitkommadivision gemacht werden soll oder nicht, wenn alles was man
weiß die Tatsache ist, dass F ein double ist.
Mark Brandis schrieb:> Mag sein. Zumindest eine Warnung könnte der Compiler aber durchaus> ausgeben, dass der Variablentyp auf der rechten Seite der Zuweisung vom> Variablentyp auf der linken Seite der Zuweisung abweicht. Das bei der> Übersetzung festzustellen kann doch so schwer nicht sein.
Ist es nicht und sowas wird ab und zu auch in Compiler eingebaut.
Vielleicht ist es sogar optional drin (-Wall und so). Aber real
existierende Programme und Programmierpraxis auch in Bezug auf
int/long/longlong und signed/unsigned führt dazu, dass man in Warnungen
geradezu ersaufen würde.
Zudem müsste man dann an vielen Stellen type casts einsetzen, wo dies
sonst nicht erforderlich ist, die dann auch noch den grössten Unfug frei
durchwinken und so ihrerseits Fehler fördern. Das ist nämlich auch so
eine Sache: Umfangreicher Einsatz von type casts behindert den Compiler
bei der Erkennung von Programmierfehlern!
A. K. schrieb:> durchwinken und so ihrerseits Fehler fördern. Das ist nämlich auch so> eine Sache: Umfangreicher Einsatz von type casts behindert den Compiler> bei der Erkennung von Programmierfehlern!
Nicht nur das, sie führen auch im Laufe eines Programmzykluses nicht
selten zu neuen Fehlern. Casts sollte man nie leichtfertig einsetzen!
Letztendes nimmt man damit dem Compiler die Typprüfung aus der Hand und
nagelt ihn auf einen Datentyp fest, den man ihm aufzwingt. Sollten sich
im Lauf eines Programmzykluses aber mal Datentypen ändern, dann kann das
sehr schnell ins Auge gehen. Denn es sind dann genau diese Casts, über
die man stolpert.
Karl Heinz Buchegger schrieb:> PS: Was hilft dir die Warnung im Farenheit-Celsius Beispiel? Der> Datentyp des Ausdrucks auf der rechten Seite ist double. Also würde> hier keine Warnung kommen!
Aber "spannenderweise" hin und wieder das falsche Ergebnis...
Das Problem rührt daher, dass C im Gegensatz zu vielen anderen Program-
miersprachen nur einen Divisionsoperator für zwei unterschiedliche
Operationen, nämlich "reelwertige Division" und "Division mit Abrunden"
hat. Welche der beiden Operationen ausgeführt wird, hängt vom Kontext,
nämlich den Datentypen der beiden Operanden ab.
In Algol, Pascal, einigen Basic-Dialekten, Python 3 und vielen anderen
Sprachen macht der '/'-Operator unabhängig von den Operandentypen immer
eine reellwertige Division. Für die Division mit Abrunden ist ein zwei-
ter Operator ('div', '\' bzw. '//') vorgesehen.
Ich vermute, die C-Entwickler haben damals absichtlich auf einen eigenen
Operator für die Division mit Abrunden verzichtet, um zu verhindern,
dass die Leute aus Bequemlichkeit auch in solchen Fällen
1
inta,b,c;
2
c=a/b;
die reellwertige Division benutzen, was zwar das erwartete Ergebnis lie-
fert, bei schlecht optimierenden Compilern aber zu versteckten Perfor-
mance-Einbußen führt. Also hat man sich hier nicht an Algol, sondern an
Fortran orientiert, das ebenfalls nur einen Divisionsoperator für beide
Operationen hat.