Forum: PC-Programmierung C++ Division ergibt immer 0 ?


von Lloyd (Gast)


Lesenswert?

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

von Mark B. (markbrandis)


Lesenswert?

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;

von Lloyd (Gast)


Lesenswert?

Yes, das wars ;)

von Karl H. (kbuchegg)


Lesenswert?

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.

von Mark B. (markbrandis)


Lesenswert?

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 ;-)

von Karl H. (kbuchegg)


Lesenswert?

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.

von Mark B. (markbrandis)


Lesenswert?

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 :)

von (prx) A. K. (prx)


Lesenswert?

Vor solchen Tutoren sollte man Reissaus nehmen. Das wird nix.

von Karl H. (kbuchegg)


Lesenswert?

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.

von D. I. (Gast)


Lesenswert?

Jeder Programmieranfänger stolpert irgendwann da drüber.

Dein Tutor ist ne Flasche.

von (prx) A. K. (prx)


Lesenswert?

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!

von Karl H. (kbuchegg)


Lesenswert?

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.

von Arc N. (arc)


Lesenswert?

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);

von Vlad T. (vlad_tepesch)


Lesenswert?

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

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
Noch kein Account? Hier anmelden.