C++ Division ergibt immer 0 ?

#2140092
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;
#2140121
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 ;-)
#2140126
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.
#2140130
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 :)
#2140133
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.
#2140137
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!
#2140143
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.
#2140302
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);
#2140545
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
Moderator #2140959
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren