Warum führt die Ganzzahldivision zu einer Ganzzahl?

In der Einführung in die Programmierung haben wir gelernt, dass Sie immer eine Ganzzahl erhalten, wenn Sie zwei Ganzzahlen teilen. Um das Problem zu beheben, machen Sie mindestens eine dieser Ganzzahlen zu einem Float.

Warum versteht der Compiler nicht, dass das Ergebnis eine Dezimalzahl sein soll?

Kommentare

  • Vielleicht möchten Sie, dass das Ergebnis stattdessen eine Ganzzahl ist. Wie kann es den Unterschied erkennen?
  • Weil der C ++ – Standard dies sagt.
  • @soandos: So wie Pascal und Python es beide tun: Sie haben einen eindeutigen Ganzzahl-Divisionsoperator, aber der Standard-Divisionsoperator gibt immer das mathematisch korrekte Ergebnis zurück. (Oder – für den Pedantiker – so korrekt, wie Sie es erhalten können Einschränkungen der FP-Mathematik.)
  • Woher weiß der Compiler, dass das Ergebnis eine Dezimalzahl sein soll? Es gibt triftige Gründe, eine Ganzzahl zu wollen.
  • @soandos In Python // ist der Integer-Division-Operator und # ist ein einzeiliger Kommentar. In Pascal ist der Integer-Division-Operator das Schlüsselwort div. Beide funktionieren recht gut für ihre jeweilige Sprache ges. C macht wahrscheinlich das Schlimmste: Ein Operator, der basierend auf einem beliebigen Kontext zwei völlig unterschiedliche Dinge tun kann.

Antwort

Warum versteht der Compiler nicht, dass das Ergebnis eine Dezimalzahl sein soll?

Der C ++ – Compiler folgt einfach genau definierten und deterministischen Regeln , wie im C ++ – Standard festgelegt. Der C ++ – Standard hat diese Regeln, weil die Standardkomitee hat beschlossen, dies so zu machen.

Sie hätten den Standard so schreiben können, dass ganzzahlige Mathematik zu Gleitkommazahlen führt, oder dies nur in Der Fall eines Restes. Dies erhöht jedoch die Komplexität: Entweder muss ich im Voraus wissen, was das Ergebnis ist, oder ich muss möglicherweise wieder in eine Ganzzahl konvertieren, wenn dies immer einen Gleitkommawert ergibt. Vielleicht möchte ich eine Ganzzahl

Eine der Kernphilosophien von C ++ lautet: „Sie zahlen nicht für das, was Sie nicht verwenden.“ . “ Wenn Sie tatsächlich die Komplexität des Mischens von Ganzzahlen und Gleitkommazahlen möchten (und die zusätzlichen CPU-Anweisungen und Speicherzugriffe, die dies mit 1 verbunden sind), führen Sie die in Ihrer Frage erwähnte Typumwandlung durch . Andernfalls halten Sie sich an die Standard-Ganzzahlmathematik.

Schließlich kann das Mischen von Integral- und Gleitkommavariablen zu Genauigkeitsverlusten und manchmal zu falschen Ergebnissen führen, wie unten erläutert. Wenn Sie dies möchten, zahlen Sie dafür: Andernfalls schreibt der Standard vor, dass Compiler strenge Regeln für das Mischen von Datentypen einhalten. Dies ist ein genau definiertes Verhalten: Als C ++ – Entwickler kann ich dies im Standard nachschlagen und sehen, wie es funktioniert.


Es gibt im Wesentlichen drei Möglichkeiten, das zu tun, was Sie versuchen. jedes mit Vor- und Nachteilen.

  • Ganzzahlige Mathematik: Dies führt dazu, dass die Ergebnisse während der Division abgeschnitten werden, wie Sie herausgefunden haben. Wenn Sie den Dezimalteil möchten, müssen Sie diesen separat behandeln, indem Sie ihn teilen, den Rest abrufen und den Dezimalteil als den durch den Divisor geteilten Rest behandeln. Dies ist eine etwas komplexere Operation und es müssen mehr Variablen jongliert werden.

  • Gleitkomma-Mathematik: Dies führt im Allgemeinen zu korrekten (ausreichenden) Ergebnissen für kleine Werte, kann dies jedoch Führen Sie Fehler leicht mit Präzision und Rundung ein, insbesondere wenn der Exponent zunimmt. Wenn Sie eine große Zahl durch eine kleine Zahl teilen, kann dies sogar zu einem Unterlauf führen oder einfach zu einem falschen Ergebnis führen, da die Skalen der Zahlen nicht gut miteinander spielen.

  • Rechnen Sie selbst. Es gibt Klassen, die erweiterte Genauigkeit von Dezimal- und rationalen Zahlen verarbeiten. Diese sind in der Regel langsamer als Mathematik bei integrierten Typen, sind jedoch im Allgemeinen immer noch recht schnell und bieten Mathematik mit beliebiger Genauigkeit. Rundungen und andere Probleme sind nicht automatisch wie bei IEEE-Floats, aber Sie erhalten mehr Kontrolle und mit Sicherheit mehr Genauigkeit.

Der Schlüssel hier ist die Auswahl basierend auf der Problemdomäne . Alle drei Methoden zur Darstellung von Zahlen haben ihre eigenen Vor- und Nachteile. Verwenden Sie einen Schleifenzähler? Wählen Sie einen integralen Typ. Orte im 3D-Raum darstellen? Wahrscheinlich eine Gruppe von Schwimmern. Möchten Sie Geld verfolgen? Verwenden Sie einen Typ mit fester Dezimalzahl.


1 Die beliebtesten CPU-Architekturen (z. B. x86-64 ) verfügt über separate Befehlssätze, die mit verschiedenen Registertypen wie Ganzzahl und Gleitkomma arbeiten, sowie über zusätzliche Befehle zum Konvertieren zwischen Integral, Gleitkomma und verschiedenen Darstellungen davon (vorzeichenbehaftet und vorzeichenlos, Gleitkomma und Doppel). Einige dieser Vorgänge können auch Speicherzugriff beinhalten: Konvertieren Sie einen Wert und speichern Sie ihn im Speicher (seiner Variablen). Mathematik auf CPU-Ebene ist nicht so einfach wie „Integer In, Float Out“.“Während das Hinzufügen von zwei Ganzzahlen eine sehr einfache Operation sein kann, möglicherweise eine einzelne Anweisung, kann das Mischen von Datentypen die Komplexität erhöhen.

Kommentare

  • Sie sagen, der C ++ – Standard schreibt vor, dass dieses Verhalten so sein sollte. Warum? Würde ‚ es nicht einfacher machen, “ Die Division von Ganzzahlen, die nicht gleichmäßig teilbar sind, führt zu Floats. Jede andere Division ist ein faires Spiel. “
  • @ moonman239 siehe meine Änderungen.
  • @ moonman239 Nicht für Compiler-Writer. Viele häufig verwendete CPU-Architekturen liefern ein ganzzahliges Ergebnis, wenn sie aufgefordert werden, eine Division mit zwei ganzzahligen Ergebnissen durchzuführen. Sie müssten eine Prüfung auf nicht ganzzahlige Ergebnisse durchführen und dann zur Verwendung des langsameren Gleitkommas wechseln Alternativ hätten sie standardmäßig die Gleitkommadivision verwenden und das Interesse derer verlieren können, die schnelle Mathematik wollten, derer, die genaue Mathematik wollten, und derer, die an C. gewöhnt waren ist ‚ keine Option, da dies die Kompatibilität mit vorhandenem Code beeinträchtigen würde.
  • Nicht, dass Sie dies als Alternative befürworten würden, sondern den statischen Typ eines Ausdrucks festlegen würden abhängig von den Laufzeitwerten der Operanden ‚ funktioniert nicht mit dem statischen Typsystem von C ++ ‚.
  • @ moonman239: Eine Operation zu haben, die abhängig von den Werten der Operanden einen anderen Typ erzeugt, ist Wahnsinn.

Antwort

Dies ist auf die Weiterentwicklung der Hardware zurückzuführen. In den frühen Tagen der Computer hatten nicht alle Maschinen eine Gleitkommaeinheit, die Hardware war einfach nicht in der Lage, den Begriff einer Gleitkommazahl zu verstehen. Natürlich können Gleitkommazahlen als Software-Abstraktion implementiert werden, aber das hat erhebliche Nachteile. Die gesamte Arithmetik auf diesen Maschinen musste standardmäßig eine reine Ganzzahlarithmetik sein.

Und noch heute gibt es eine feste Unterscheidung zwischen Ganzzahl- und Gleitkomma-Recheneinheiten innerhalb einer CPU. Ihre Operanden werden zunächst in separaten Registerdateien gespeichert, und eine Ganzzahleinheit wird so verdrahtet, dass sie zwei Ganzzahlargumente verwendet und ein Ganzzahlergebnis erzeugt, das in einem Ganzzahlregister endet. Einige CPUs erfordern sogar, dass ein ganzzahliger Wert im Speicher gespeichert und dann wieder in ein Gleitkommaregister geladen wird, bevor er in eine Gleitkommazahl umcodiert werden kann, bevor Sie eine Gleitkommadivision durchführen können.

Daher war die Entscheidung, die die C-Entwickler am Anfang der Sprache getroffen haben (C ++ hat dieses Verhalten einfach geerbt), die einzig angemessene Entscheidung und bleibt heute von Wert: Wenn Sie Gleitkomma-Mathematik benötigen, sind Sie es kann es benutzen. Wenn Sie es nicht brauchen, müssen Sie es auch nicht.

Kommentare

  • Es ist traurig, dass die meisten Einschränkungen bei der Die Erstellung des C ++ – Standards ist heute ziemlich veraltet! Beispiel: “ Sie zahlen nicht für das, was Sie nicht verwenden. “ Heutzutage wird Hardware als selbstverständlich angesehen und alle Benutzer möchten Ausführung!
  • @ mahen23 Nicht alle Benutzer denken so. Ich arbeite in einem Bereich, in dem Programme parallel auf Tausenden von CPU-Kernen ausgeführt werden. In diesem Bereich ist Effizienz Geld, sowohl in Bezug auf Investitionen als auch in Bezug auf den bloßen Stromverbrauch. Eine Sprache wie Java hat in diesem Bereich keine Chance, während C ++ dies tut.
  • @ mahen23 Nein, es ist nicht ‚ t – oder besser Nur wenn Sie sich die aktuellen CPU-Architekturen für Desktops und höher ansehen. Es gibt immer noch viele eingebettete Systeme, die ‚ nicht oder nur teilweise Gleitkommaoperationen unterstützen, und C sowie C ++ unterstützen sie weiterhin, um eine möglichst effiziente Implementierung zu gewährleisten mit Assembler. Übrigens unterscheiden noch übergeordnete Sprachen wie Python zwischen Ganzzahl- und FP-Operationen – versuchen Sie 10 / 3.

Antwort

10/2 mit ganzen Zahlen ergibt genau 5 – die richtige Antwort.

Mit Gleitkomma-Mathematik kann 10/2 die richtige geben Antwort *.

Mit anderen Worten, es ist unmöglich, dass Gleitkommazahlen auf der aktuellen Hardware „perfekt“ sind – nur ganzzahlige Mathematik kann korrekt sein, leider kann sie keine Dezimalstellen ausführen, aber es gibt einfache Arbeit arounds.

Führen Sie beispielsweise anstelle von 4/3 (4 * 1000) / (3 * 1000) == 1333 aus. Zeichnen Sie einfach ein. in der Software, wenn Sie Ihrem Benutzer die Antwort anzeigen (1.333). Dies gibt Ihnen eine genaue Antwort anstelle einer Antwort, die um eine bestimmte Anzahl von Dezimalstellen falsch ist.

Gleitkomma-Rechenfehler können zu erheblichen Fehlern führen – alles Wichtige (wie Finanzen) verwendet ganzzahlige Mathematik .

* Das 10/2-Beispiel ist mit Gleitkomma-Mathematik tatsächlich korrekt. aber Sie können sich nicht darauf verlassen, viele andere Zahlen liefern falsche Ergebnisse …Weitere Informationen finden Sie unter: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps Der Punkt ist, dass Sie sich nicht auf Genauigkeit verlassen können, wenn Gleitkommazahlen vorhanden sind Beteiligte

Kommentare

  • IEEE 754-konforme Gleitkomma-Implementierungen liefern Ihnen ein genaues Ergebnis für 10 / 2. Tatsächlich geben sie Ihnen ein genaues Ergebnis Ergebnisse für jede Operation, an der nur ganzzahlige Operanden beteiligt sind, die ein ganzzahliges Ergebnis haben, vorausgesetzt, Operanden und Ergebnis können genau dargestellt werden, was „klein genug“ sein kann.
  • @ 5gon12eder dort ‚ muss nicht ausgewählt werden, ich ‚ versuche nur, ein komplexes Problem in einfachen Worten zu beschreiben. Der gesamte Sinn der Unterstützung von nicht ganzzahligen Werten besteht darin, Dezimalstellen zu haben ( Dies kann mit ganzen Zahlen geschehen, indem einfach alles mit der Anzahl der Dezimalstellen multipliziert wird, die Sie möchten, wie ich es demsonstraed habe.

Antwort

Obwohl technisch nicht vollständig korrekt, C ++ wird immer noch als Obermenge von C betrachtet, wurde von ihm inspiriert und hat als solcher einige seiner Eigenschaften übernommen, wobei die Ganzzahldivision eine davon ist.

C wurde hauptsächlich so konzipiert, dass es effizient und schnell ist, und Ganzzahlen sind im Allgemeinen viel schneller als Gleitkommawerte, da der Ganzzahltyp an die Hardware gebunden ist, während Gleitkommawerte berechnet werden müssen.

Wenn der Operand / zwei Ganzzahlen empfängt, eine Von der linken Seite und einer auf der rechten Seite wird möglicherweise überhaupt keine Division durchgeführt. Das Ergebnis kann mithilfe einer einfachen Addition und einer Schleife berechnet werden, wobei gefragt wird, wie oft der Operand auf der rechten Seite in den Operanden auf der linken Seite passt.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.