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.
Jeder Programmieranfänger stolpert irgendwann da drüber. Dein Tutor ist ne Flasche.
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...
1 | double a; |
2 | a = 4 / 8 + 1 / 5.0; |
3 | |
4 | // Welche Variante(n) liefern 41 °F?
|
5 | double c = 5.0; |
6 | double f1 = 9 / 5 * c + 32; |
7 | double f2 = c * 9 / 5 + 32; |
8 | double f3 = c * ( 9 / 5 + 32 / c); |
9 | double f4 = c * ( 9 / 5.0 + 32.0 / c); |
Arc Net schrieb: > Aber "spannenderweise" hin und wieder das falsche Ergebnis...double a; > a = 4 / 8 + 1 / 5.0; > > // Welche Variante(n) liefern 41 °F? ohne compiler > double c = 5.0; c=5.0 > double f1 = 9 / 5 * c + 32; f1 = 1*5.0+32 = 37 > double f2 = c * 9 / 5 + 32; f2 = 45.0/5 + 32 = 9.0 + 32 = 41 > double f3 = c * ( 9 / 5 + 32 / c); f3 = 5.0 * (1 + 6.4) = 5.0 * 7.4 = 37 > double f4 = c * ( 9 / 5.0 + 32.0 / c); f4 = 5.0 * (1.8 + 6.4) = 5.0 * 8.2 = 41
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 | int a, 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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.