Warum ergibt das Teilen von zwei int nicht den richtigen Wert, wenn es dem Double zugewiesen wird?

108

Wie kommt es, dass im folgenden Ausschnitt

int a = 7;
int b = 3;
double c = 0;
c = a / b;

cendet damit, dass man den Wert 2 hat, anstatt wie erwartet 2,3333. Wenn aund bdoppelt sind, lautet die Antwort 2.333. Aber sicher, weil es c bereits ein Double ist, hätte es mit ganzen Zahlen funktionieren sollen?

Wie kommt int/int=doublees, dass es nicht funktioniert?

Jahoe
quelle
Mögliches Duplikat des Divisionsergebnisses ist immer Null
phuclv

Antworten:

160

Dies liegt daran, dass Sie die Integer-Division-Version von verwenden operator/, die 2 ints dauert und eine zurückgibt int. Um die doubleVersion zu verwenden, die a zurückgibt double, muss mindestens eines der ints explizit in a umgewandelt werden double.

c = a/(double)b;
Chad La Guardia
quelle
9
Ich würde es vorziehen ausdrücklich sowohl zu konvertieren aund bzu doublefür Klarheit einfach, aber es ist wirklich egal.
John Dibling
31
Da die Frage mit C ++ markiert ist, würde ich lieber static_cast <> als C-Cast sehen.
Martin York
16
Persönlich bin ich der Meinung, dass die Casts im C-Stil klarer sind (das Casting in den meisten anderen gängigen Sprachen erfolgt im C-Stil). static_cast<>kam mir immer langatmig vor. Bei Primitiven besteht keine wirkliche Gefahr des Verwechselns static_cast<>und reinterpret_cast<>Verwechselns.
Chad La Guardia
6
@ Tux-D: Für arithmetische Casts? Ich würde es static_castin diesem Fall vorziehen, zu vermeiden und stattdessen eine Besetzung im C-Stil zu verwenden. Die Verwendung von Casts im C ++ - Stil hat hier keinen Vorteil, und sie überladen den Code viel mehr als Casts im C-Stil. Arithmetische Besetzung ist genau der Kontext, in dem Besetzungen im C-Stil perfekt und tatsächlich geeigneter sind als andere Besetzungen.
Am
18
Manchmal kann man die "No C-Style-Cast" -Leute durch Schreiben überlisten double(b). Sie erkennen nicht immer, dass es sich um eine Konvertierung handelt, da sie genauso aussieht wie ein expliziter Konstruktoraufruf.
Steve Jessop
12

Hier ist es:

a) Das Teilen von zwei ints führt immer eine ganzzahlige Division durch. Das Ergebnis von a/bin Ihrem Fall kann also nur ein seinint .

Wenn Sie behalten aund bals ints, aber sie vollständig teilen möchten , müssen Sie mindestens eine davon zum Verdoppeln wirken: (double)a/boder a/(double)boder(double)a/(double)b .

b) cein double, so kann sie akzeptieren einen intWert auf assignement: die intautomatisch umgewandelt doubleund übertragen aufc .

c) Denken Sie daran, dass bei der Zuweisung der Ausdruck rechts von zuerst= berechnet wird (gemäß Regel (a) oben und ohne Berücksichtigung der Variablen links von ) und dann der Variablen links von (gemäß ( b) oben). Ich glaube, das vervollständigt das Bild.==

nplatis
quelle
11

Mit sehr wenigen Ausnahmen (ich kann mir nur eine vorstellen) bestimmt C ++ die gesamte Bedeutung eines Ausdrucks (oder Unterausdrucks) aus dem Ausdruck selbst. Was Sie mit den Ergebnissen des Ausdrucks machen, spielt keine Rolle. In Ihrem Fall ist im Ausdruck a / bkein doublein Sicht; alles ist int. Der Compiler verwendet also die Ganzzahldivision. Erst wenn das Ergebnis vorliegt, wird überlegt, was damit zu tun ist, und es wird in konvertiert double.

James Kanze
quelle
3
Die einzige Ausnahme, die mir einfällt, ist die Auswahl einer Funktionsüberladung, wenn ein Zeiger verwendet wird. Der Wert von &funcnamehängt davon ab, in welchen Typ Sie ihn umwandeln.
Steve Jessop
2
@ Steve Jessop Das ist die einzige Ausnahme, an die ich auch denken kann. (Aber angesichts der Größe und Komplexität des Standards möchte ich nicht schwören, dass ich keine verpasst habe.)
James Kanze
6

cist eine doubleVariable, aber der ihr zugewiesene Wert ist ein intWert, da er sich aus der Division von zwei ints ergibt, wodurch Sie eine "ganzzahlige Division" erhalten (den Rest fallen lassen). Was also in der Zeile passiert, c=a/bist

  1. a/b wird ausgewertet, wodurch ein temporärer Typ erstellt wird int
  2. Der Wert des Temporären wird cnach der Konvertierung in Typ zugewiesen double.

Der Wert von a/bwird ohne Bezugnahme auf seinen Kontext (Zuordnung zu double) bestimmt.

Fred Foo
quelle
6

Wenn Sie zwei Ganzzahlen teilen, ist das Ergebnis eine Ganzzahl, unabhängig davon, ob Sie sie in einem Doppel speichern.

Alok Speichern
quelle
5

In der C ++ - Sprache wird das Ergebnis des Unterausdrucks niemals vom umgebenden Kontext beeinflusst (mit einigen seltenen Ausnahmen). Dies ist eines der Prinzipien, denen die Sprache sorgfältig folgt. Der Ausdruck c = a / benthält einen unabhängigen Unterausdruck a / b, der unabhängig von etwas außerhalb dieses Unterausdrucks interpretiert wird. Der Sprache ist es egal, dass Sie das Ergebnis später einem zuweisen double.a / bist eine ganzzahlige Division. Alles andere spielt keine Rolle. Sie werden sehen, dass dieses Prinzip in vielen Ecken der Sprachspezifikation befolgt wird. So funktioniert C ++ (und C).

Ein Beispiel für eine oben erwähnte Ausnahme ist die Zuweisung / Initialisierung von Funktionszeigern in Situationen mit Funktionsüberladung

void foo(int);
void foo(double);

void (*p)(double) = &foo; // automatically selects `foo(fouble)`

Dies ist ein Kontext, in dem die linke Seite einer Zuweisung / Initialisierung das Verhalten der rechten Seite beeinflusst. (Die Initialisierung des Verweises auf das Array verhindert auch den Zerfall des Array-Typs, was ein weiteres Beispiel für ein ähnliches Verhalten ist.) In allen anderen Fällen ignoriert die rechte Seite die linke Seite vollständig.

Ameise
quelle
4

Der /Operator kann für die Ganzzahldivision oder die Gleitkommadivision verwendet werden. Sie geben ihm zwei ganzzahlige Operanden, also wird eine ganzzahlige Division durchgeführt, und dann wird das Ergebnis in einem Doppel gespeichert.

Vicky
quelle
2

Dies ist technisch sprachabhängig, aber fast alle Sprachen behandeln dieses Thema gleich. Wenn zwischen zwei Datentypen in einem Ausdruck eine Typinkongruenz besteht, versuchen die meisten Sprachen, die Daten auf einer Seite des Ausdrucks so umzuwandeln, dass sie =mit den Daten auf der anderen Seite gemäß einem Satz vordefinierter Regeln übereinstimmen.

Wenn Sie zwei Zahlen desselben Typs (Ganzzahlen, Doppelte usw.) teilen, ist das Ergebnis immer vom gleichen Typ (also führt 'int / int' immer zu int).

In diesem Fall haben Sie, double var = integer result was das ganzzahlige Ergebnis nach der Berechnung auf ein Doppel umwandelt. In diesem Fall gehen die Bruchdaten bereits verloren. (Die meisten Sprachen führen dieses Casting durch, um Typungenauigkeiten zu vermeiden, ohne eine Ausnahme oder einen Fehler auszulösen.)

Wenn Sie das Ergebnis als Doppel behalten möchten, möchten Sie eine Situation schaffen, in der Sie haben double var = double result

Der einfachste Weg, dies zu tun, besteht darin, den Ausdruck auf der rechten Seite einer Gleichung zu zwingen, ihn zu verdoppeln:

c = a/(double)b

Die Trennung zwischen einer Ganzzahl und einem Doppel führt dazu, dass die Ganzzahl in das Doppel umgewandelt wird (beachten Sie, dass der Compiler beim Rechnen häufig auf den spezifischsten Datentyp "hochgespielt" wird, um Datenverlust zu vermeiden).

Nach dem Upcast awird als Doppel beendet und jetzt haben Sie die Aufteilung zwischen zwei Doppel. Dadurch wird die gewünschte Unterteilung und Zuordnung erstellt.

WIEDER, bitte beachten Sie, dass dies sprachspezifisch ist (und sogar compilerspezifisch sein kann), jedoch behandeln fast alle Sprachen (sicherlich alle, die mir auf den ersten Blick einfallen) dieses Beispiel identisch.

matthewdunnam
quelle
Diese Frage ist mit [C ++] gekennzeichnet, und der C ++ - Standard schreibt genau vor, wie dies funktioniert. Ich bin mir nicht sicher, was Sie unter "sprachspezifisch" verstehen, und es ist sicherlich nicht compilerspezifisch, vorausgesetzt, es werden keine Compiler-Erweiterungen verwendet.
John Dibling
Es ist auch falsch zu sagen, dass "double var = integer result, das die double var auf int reduziert". Das Double wird nicht in ein Int umgewandelt. Das int-Ergebnis wird in ein Double umgewandelt.
John Dibling
Ich habe die Möglichkeit von Compiler-Erweiterungen zugelassen (ich hatte dieses Problem tatsächlich einmal, als meine Umgebung die Ergebnisse "falsch goss" und ich nicht herausfinden konnte, warum). Das Ergebnis ist sprachspezifisch, da in einigen Sprachen nicht dieselben Casting-Regeln gelten. Ich habe nicht gedacht, dass es sich um ein C ++ - spezifisches Tag handelt. Sie haben Recht mit dem Kommentar "double var = integer result". Bearbeitet, um das widerzuspiegeln. Danke dir!
Matthewdunnam
0

Wichtig ist, dass eines der Berechnungselemente ein Float-Double-Typ ist. Um ein doppeltes Ergebnis zu erhalten, müssen Sie dieses Element wie unten gezeigt umwandeln:

c = static_cast<double>(a) / b;

oder c = a / static_cast (b);

Oder Sie können es direkt erstellen ::

c = 7.0 / 3;

Beachten Sie, dass eines der Berechnungselemente die '.0' haben muss, um eine Division eines Float-Double-Typs durch eine Ganzzahl anzuzeigen. Andernfalls ist das Ergebnis auch Null, obwohl die Variable c ein Doppel ist.

TheArquitect
quelle
Was bringt Ihre Antwort, dass keine der anderen 9 Antworten noch nicht vorhanden ist?
Bolov