Was kann mit Programmiersprachen getan werden, um Gleitkommafallen zu vermeiden?


28

Das Missverständnis der Gleitkomma-Arithmetik und ihrer Mängel ist eine Hauptursache für Überraschung und Verwirrung in der Programmierung. Angesichts der Tatsache, dass viele Programmierer die Auswirkungen noch nicht verstanden haben, besteht die Möglichkeit, dass viele subtile Fehler (insbesondere in Finanzsoftware) auftreten. Was können Programmiersprachen tun, um ihre Fallstricke für diejenigen zu vermeiden, die mit den Konzepten nicht vertraut sind, und gleichzeitig ihre Geschwindigkeit anzubieten, wenn Genauigkeit für diejenigen, die die Konzepte verstehen, nicht kritisch ist ?


26
Das Einzige, was eine Programmiersprache tun kann, um die Fallstricke der Gleitkommaverarbeitung zu vermeiden, ist, sie zu verbieten. Beachten Sie, dass dies auch Gleitkommazahlen zur Basis 10 umfasst, was im Allgemeinen genauso ungenau ist, mit der Ausnahme, dass Finanzanwendungen an diese angepasst sind.
David Thornley

4
Dafür ist "Numerical Analysis" gedacht. Erfahren Sie, wie Sie den Präzisionsverlust minimieren - auch bekannt als Fließkomma-Fallstricke.

Ein gutes Beispiel für ein Gleitkomma-Problem: stackoverflow.com/questions/10303762/0-0-0-0-0
Austin Henley

Antworten:


47

Sie sagen, "speziell für Finanzsoftware", was einen meiner Lieblingsbeschwerden hervorruft: Geld ist kein Float, es ist ein Int .

Klar, es sieht aus wie ein Schwimmer. Dort steht ein Dezimalpunkt. Aber das liegt nur daran, dass Sie an Einheiten gewöhnt sind, die das Problem verwirren. Geld kommt immer in ganzzahligen Mengen. In Amerika sind es Cent. (In bestimmten Zusammenhängen denke ich, dass es Mühlen sein können , aber ignorieren Sie das fürs Erste.)

Wenn Sie also 1,23 Dollar sagen, sind das wirklich 123 Cent. Immer, immer, immer rechnen, und es wird dir gut gehen. Weitere Informationen finden Sie unter:

Bei der direkten Beantwortung der Frage sollten Programmiersprachen nur einen Money-Typ als vernünftiges Grundelement enthalten.

aktualisieren

Ok, ich hätte nur zweimal "immer" sagen sollen, nicht dreimal. Geld ist in der Tat immer ein int; diejenigen, die anders denken, können gerne versuchen, mir 0,3 Cent zu schicken und mir das Ergebnis auf Ihrem Kontoauszug zu zeigen. Kommentatoren weisen jedoch darauf hin, dass es seltene Ausnahmen gibt, wenn Sie Gleitkomma-Berechnungen für geldähnliche Zahlen durchführen müssen. ZB bestimmte Arten von Preisen oder Zinsberechnungen. Auch dann sollten diese wie Ausnahmen behandelt werden. Geld kommt rein und geht raus als ganzzahlige Mengen. Je näher Ihr System dem kommt, desto vernünftiger wird es.


20
@JoelFan: Sie verwechseln ein Konzept mit einer plattformspezifischen Implementierung.
Whatsisname

12
So einfach ist das nicht. Zinsberechnungen führen unter anderem zu Bruchteilen von Cent und müssen irgendwann nach einer festgelegten Methode gerundet werden.
Kevin Cline

24
Fiktional -1, da mir der Repräsentant für eine Gegenstimme fehlt :) ... Dies mag für alles in Ihrer Brieftasche richtig sein, aber es gibt viele Buchhaltungssituationen, in denen Sie durchaus mit Zehntel-Cent-Beträgen oder kleineren Brüchen zu tun haben könnten. Decimalist das einzig vernünftige System, um damit umzugehen, und Ihr Kommentar "Ignoriere das jetzt" ist der Vorbote des Schicksals für Programmierer überall: P
Detly,

9
@ Kevin Cline: In Berechnungen gibt es gebrochene Cent, aber es gibt Konventionen, wie man damit umgeht. Das Ziel für Finanzberechnungen ist nicht die mathematische Korrektheit, sondern es werden genau die gleichen Ergebnisse erzielt, die ein Banker mit einem Taschenrechner erzielen würde.
David Thornley

6
Alles wird perfekt, wenn Sie das Wort "Ganzzahl" durch "rational" ersetzen -
Emilio Garavaglia

15

In vielen Fällen hilft es, Unterstützung für einen Dezimaltyp bereitzustellen. Viele Sprachen haben einen Dezimaltyp, werden jedoch nicht ausreichend verwendet.

Es ist wichtig, die Annäherung zu verstehen, die beim Arbeiten mit der Darstellung reeller Zahlen auftritt. Die Verwendung von Dezimal- und Gleitkommatypen 9 * (1/9) != 1ist eine korrekte Aussage. Bei Konstanten kann ein Optimierer die Berechnung so optimieren, dass sie korrekt ist.

Die Angabe eines ungefähren Operators würde helfen. Solche Vergleiche sind jedoch problematisch. Beachten Sie, dass .9999 Billionen Dollar ungefähr 1 Billion Dollar entsprechen. Könnten Sie bitte die Differenz auf mein Bankkonto einzahlen?


2
0.9999...Billionen Dollar entsprechen genau 1 Billion Dollar.
NUR MEINE STELLUNGNAHME

5
@JUST: Ja, aber ich habe keine Computer mit Registern angetroffen, die gespeichert werden können 0.99999.... Sie werden alle irgendwann abgeschnitten, was zu einer Ungleichung führt. 0.9999ist gleich genug für das Engineering. Aus finanziellen Gründen ist es nicht.
BillThor

2
Aber welche Art von System verwendete Billionen von Dollar als Basiseinheit anstelle von Dollar?
Brad

@Brad Berechnen Sie (1 Billion / 3) * 3 auf Ihrem Rechner. Welchen Wert bekommen Sie?
BillThor

8

Uns wurde gesagt, was wir im ersten Jahr (im zweiten Jahr) in der Informatik zu tun haben, als ich zur Universität ging (dieser Kurs war auch eine Voraussetzung für die meisten naturwissenschaftlichen Kurse).

Ich erinnere mich an den Vortragenden, der sagte: "Gleitkommazahlen sind Näherungswerte. Verwenden Sie Ganzzahlen für Geld. Verwenden Sie FORTRAN oder eine andere Sprache mit BCD-Zahlen für eine genaue Berechnung." (Und dann wies er auf die Näherung hin, wobei er das klassische Beispiel von 0,2 verwendete, das in binären Gleitkommazahlen nicht genau dargestellt werden konnte.) Dies zeigte sich auch in dieser Woche in den Laborübungen.

Dieselbe Vorlesung: "Wenn Sie mehr Genauigkeit durch Fließkommazahlen erzielen möchten, sortieren Sie Ihre Begriffe. Addieren Sie kleine Zahlen und nicht große Zahlen." Das ist mir in den Sinn gekommen.

Vor ein paar Jahren hatte ich eine sphärische Geometrie, die sehr genau und trotzdem schnell sein musste. 80-Bit-Double auf PCs schnitt nicht ab, daher habe ich dem Programm einige Typen hinzugefügt, die die Begriffe sortierten, bevor kommutative Operationen ausgeführt wurden. Problem gelöst.

Bevor Sie sich über die Qualität der Gitarre beschweren, lernen Sie, zu spielen.

Ich hatte vor vier Jahren einen Kollegen, der für JPL gearbeitet hatte. Er drückte seinen Unglauben aus, dass wir FORTRAN für einige Dinge verwendet haben. (Wir brauchten sehr genaue numerische Simulationen, die offline berechnet wurden.) "Wir haben das ganze FORTRAN durch C ++ ersetzt", sagte er stolz. Ich fragte mich nicht mehr, warum sie einen Planeten verpasst hatten.


2
+1 das richtige Werkzeug für den richtigen Job. Obwohl ich FORTRAN eigentlich nicht benutze. Zum Glück arbeite ich auch nicht an unseren Finanzsystemen.
James Khoury

"Wenn Sie mehr Genauigkeit durch Fließkommazahlen erzielen möchten, sortieren Sie Ihre Begriffe. Addieren Sie kleine Zahlen und nicht große Zahlen." Irgendeine Probe dazu?
Mamcx

@mamcx Stellen Sie sich eine dezimale Gleitkommazahl mit nur einer Stelle der Genauigkeit vor. Die Berechnung 1.0 + 0.1 + ... + 0.1(10-mal wiederholt) wird zurückgegeben, 1.0wenn jedes Zwischenergebnis gerundet wird. Es andersrum tun, Sie Zwischenergebnisse erhalten 0.2, 0.3..., 1.0und schließlich 2.0. Dies ist ein extremes Beispiel, aber bei realistischen Gleitkommazahlen treten ähnliche Probleme auf. Die Grundidee ist, dass das Hinzufügen von Zahlen ähnlicher Größe zum kleinsten Fehler führt. Beginnen Sie mit den kleinsten Zahlen, da deren Summe größer ist und sich daher besser für die Addition zu größeren eignet.
Maaartinus

Gleitkomma-Inhalte in Fortran und C ++ werden jedoch größtenteils identisch sein. Beide sind genau und offline, und ich bin mir ziemlich sicher, dass Fortran keine nativen BCD-Reals hat ...
Mark

8

Warnung: Dem Gleitkommatyp System.Double fehlt die Genauigkeit für die direkte Gleichheitsprüfung.

double x = CalculateX();
if (x == 0.1)
{
    // ............
}

Ich glaube nicht, dass irgendetwas auf Sprachniveau getan werden kann oder sollte.


1
Ich habe lange kein Float oder Double mehr benutzt, also bin ich neugierig. Ist das eine tatsächlich vorhandene Compiler-Warnung oder nur eine, die Sie gerne sehen würden?
Karl Bielefeldt

1
@ Karl - Ich persönlich habe es nicht gesehen oder brauche es, aber ich stelle mir vor, dass es für engagierte, aber umweltbewusste Entwickler nützlich sein könnte.
ChaosPandion

1
Die binären Gleitkommatypen sind qualitativ nicht besser oder schlechter als Decimalbeim Gleichheitstest. Der Unterschied zwischen 1.0m/7.0m*7.0mund 1.0mkann um viele Größenordnungen geringer sein als der Unterschied zwischen 1.0/7.0*7.0, aber er ist nicht Null.
Supercat

1
@Patrick - Ich bin mir nicht sicher, worauf du hinaus willst. Es gibt einen großen Unterschied zwischen etwas, das für einen Fall wahr ist, und etwas, das für alle Fälle wahr ist.
ChaosPandion

1
@ChaosPandion Das Problem mit dem Beispiel in diesem Beitrag ist nicht der Gleichheitsvergleich, sondern das Gleitkomma-Literal. Es gibt keinen Schwimmer mit dem genauen Wert 1,0 / 10. Gleitkomma-Mathematik führt zu 100% genauen Ergebnissen, wenn mit ganzzahligen Zahlen gerechnet wird, die in die Mantisse passen.
Patrick

7

Standardmäßig sollten Sprachen Rationen mit beliebiger Genauigkeit für nicht ganzzahlige Zahlen verwenden.

Diejenigen, die optimieren müssen, können immer nach Schwimmern fragen. In C und anderen Programmiersprachen als Standardeinstellung sinnvoll, in den meisten heute verbreiteten Sprachen jedoch nicht.


1
Wie gehen Sie dann mit irrationalen Zahlen um?
Dsimcha

3
Sie machen das genauso wie bei Floats: Approximation.
Waquo

1
Ich muss sagen, dass ich denke, dass dies sehr sinnvoll ist. Die meisten Menschen, die genaue Zahlen benötigen, benötigen Rationales und nicht Irrationales (Wissenschaft und Technik können Irrationales verwenden, aber Sie kehren dann wieder in den ungefähren Bereich zurück, oder Sie führen eine ganz spezielle reine Mathematik durch).
jk.

1
Berechnungen mit beliebig genauen Rationen sind häufig um Größenordnungen langsamer (möglicherweise VIELE Größenordnungen langsamer) als Berechnungen mit einer Hardware-unterstützten double. Wenn eine Berechnung auf einen Teil pro Million genau sein muss, ist es besser, eine Mikrosekunde damit zu verbringen, sie auf wenige Teile pro Milliarde genau zu berechnen, als eine Sekunde damit, sie absolut genau zu berechnen.
Superkatze

5
@supercat: Was Sie vorschlagen, ist nur ein Aushängeschild vorzeitiger Optimierung. Die gegenwärtige Situation ist, dass die überwiegende Mehrheit der Programmierer überhaupt kein Bedürfnis nach schneller Mathematik hat und dann von schwer zu verstehendem Gleitkomma-Verhalten (Fehlverhalten) gebissen wird, so dass die relativ kleine Anzahl von Programmierern, die schnelle Mathematik benötigen, es ohne es zu haben bekommt um ein einzelnes zusätzliches Zeichen einzugeben. Das machte in den siebziger Jahren Sinn, jetzt ist es nur noch Unsinn. Die Standardeinstellung sollte sicher sein. Wer schnell braucht, sollte danach fragen.
Waquo

4

Die zwei größten Probleme mit Gleitkommazahlen sind:

  • inkonsistente Einheiten, die auf die Berechnungen angewendet werden (beachten Sie, dass dies in gleicher Weise auch die Ganzzahlarithmetik beeinflusst)
  • Unverständnis, dass FP-Zahlen eine Annäherung sind und wie intelligent mit Rundungen umgegangen werden kann.

Der erste Fehlertyp kann nur behoben werden, indem ein zusammengesetzter Typ angegeben wird, der Wert- und Einheiteninformationen enthält. Zum Beispiel ein lengthoder ein areaWert, der die Einheit enthält (Meter oder Quadratmeter oder Fuß bzw. Quadratfuß). Andernfalls müssen Sie fleißig damit umgehen, immer mit einer Maßeinheit zu arbeiten und nur dann in eine andere umzurechnen, wenn wir die Antwort mit einem Menschen teilen.

Der zweite Fehlertyp ist ein konzeptioneller Fehler. Die Misserfolge manifestieren sich, wenn die Menschen sie als absolute Zahlen betrachten. Dies wirkt sich auf Gleichheitsoperationen, kumulative Rundungsfehler usw. aus. Beispielsweise kann es richtig sein, dass für ein System zwei Messungen innerhalb einer bestimmten Fehlergrenze äquivalent sind. Dh .999 und 1.001 sind ungefähr gleich wie 1.0, wenn Sie sich nicht für Unterschiede interessieren, die kleiner als +/- .1 sind. Allerdings sind nicht alle Systeme so nachsichtig.

Wenn eine Sprachniveauregelung benötigt wird, würde ich das Gleichheitspräzision nennen . In NUnit, JUnit und ähnlich aufgebauten Test-Frameworks können Sie die Genauigkeit steuern, die als korrekt angesehen wird. Beispielsweise:

Assert.That(.999, Is.EqualTo(1.001).Within(10).Percent);
// -- or --
Assert.That(.999, Is.EqualTo(1.001).Within(.1));

Wenn beispielsweise C # oder Java geändert wurden, um einen Präzisionsoperator einzuschließen, könnte dies ungefähr so ​​aussehen:

if(.999 == 1.001 within .1) { /* do something */ }

Wenn Sie jedoch eine solche Funktion bereitstellen, müssen Sie auch den Fall berücksichtigen, in dem die Gleichheit gut ist, wenn die +/- Seiten nicht gleich sind. Zum Beispiel würde + 1 / -10 zwei Zahlen als äquivalent betrachten, wenn eine von ihnen innerhalb von 1 mehr oder 10 weniger als die erste Zahl wäre. Um diesen Fall zu behandeln, müssen Sie möglicherweise auch ein rangeSchlüsselwort hinzufügen :

if(.999 == 1.001 within range(.001, -.1)) { /* do something */ }

2
Ich würde die Reihenfolge ändern. Das konzeptionelle Problem ist allgegenwärtig. Das Problem der Einheitenumrechnung ist vergleichsweise gering.
S.Lott

Ich mag das Konzept eines Präzisionsoperators, aber wie Sie weiter unten erwähnen, müsste es auf jeden Fall gut durchdacht sein. Persönlich wäre ich eher geneigt, es als sein eigenes vollständiges syntaktisches Konstrukt zu betrachten.
ChaosPandion

Es könnte auch sehr einfach in einer Bibliothek gemacht werden.
Michael K

1
@ dan04: Ich dachte eher an "alle Berechnungen auf ein Prozent genau" oder ähnliches. Ich habe die Teergrube gesehen, die Maßeinheit Umgang ist, und ich bleibe weit weg.
TMN

1
Vor ungefähr 25 Jahren sah ich ein numerisches Paket mit einem Typ, der aus einem Paar Gleitkommazahlen besteht, die den maximal und minimal möglichen Wert für eine Menge darstellen. Während die Zahlen die Berechnungen durchlaufen, würde die Differenz zwischen Maximum und Minimum zunehmen. Tatsächlich konnte so festgestellt werden, wie genau ein berechneter Wert tatsächlich war.
Supercat

3

Was können Programmiersprachen? Ich weiß nicht, ob es eine Antwort auf diese Frage gibt, da alles, was der Compiler / Interpreter im Auftrag des Programmierers unternimmt, um sein / ihr Leben zu erleichtern, normalerweise der Leistung, der Klarheit und der Lesbarkeit zuwiderläuft. Ich denke, sowohl der C ++ - Weg (zahle nur für das, was du brauchst) als auch der Perl-Weg (Prinzip der geringsten Überraschung) sind gültig, aber es hängt von der Anwendung ab.

Programmierer müssen immer noch mit der Sprache arbeiten und verstehen, wie sie mit Gleitkommawerten umgeht. Wenn dies nicht der Fall ist, werden sie Annahmen treffen, und eines Tages wird das verfolgte Verhalten nicht mit ihren Annahmen übereinstimmen.

Meine Einstellung zu dem, was der Programmierer wissen muss:

  • Welche Gleitkommatypen sind auf dem System und in der Sprache verfügbar?
  • Welcher Typ wird benötigt?
  • Wie drückt man die Absichten aus, welche Art im Code benötigt wird?
  • Wie Sie die Vorteile einer automatischen Typenwerbung richtig nutzen, um Klarheit und Effizienz bei gleichzeitiger Wahrung der Korrektheit in Einklang zu bringen

3

Was können Programmiersprachen tun, um [Gleitkomma-] Fallstricke zu vermeiden?

Verwenden Sie sinnvolle Standardeinstellungen, z. B. integrierte Unterstützung für Decmials.

Groovy macht das ganz gut, obwohl Sie mit ein wenig Aufwand immer noch Code schreiben können, um die Gleitkomma-Ungenauigkeit einzuführen.


3

Ich bin damit einverstanden, dass es auf sprachlicher Ebene nichts zu tun gibt. Programmierer müssen verstehen, dass Computer diskret und begrenzt sind und dass viele der in ihnen dargestellten mathematischen Konzepte nur Näherungswerte sind.

Egal Fließkomma. Man muss verstehen, dass die Hälfte der Bitmuster für negative Zahlen verwendet wird und dass 2 ^ 64 tatsächlich ziemlich klein ist, um typische Probleme mit der Ganzzahlarithmetik zu vermeiden.


Nicht einverstanden, die meisten Sprachen bieten derzeit zu viel Unterstützung für binäre Gleitkommatypen (warum ist == sogar für Gleitkommazahlen definiert?) und nicht genug Unterstützung für rationale oder dezimale Werte
jk.

@jk: Auch wenn das Ergebnis einer Berechnung niemals mit dem Ergebnis einer anderen Berechnung übereinstimmen würde, wäre ein Vergleich der Gleichheit für den Fall nützlich, dass zwei Variablen derselbe Wert zugewiesen wird (obwohl die allgemein implementierten Gleichheitsregeln möglicherweise zutreffen) zu locker, da x== ynicht impliziert, dass das Durchführen einer Berechnung für xdasselbe Ergebnis wie das Durchführen derselben Berechnung für ergibt y.
Superkatze

@supercat du brauchst immer noch einen Vergleich, aber ich möchte lieber, dass ich in der Sprache eine Toleranz für jeden Gleitkomma-Vergleich spezifiziere. Dann kann ich immer noch zur Gleichheit zurückkehren, indem ich Toleranz = 0 wähle, aber ich bin zumindest gezwungen, dies zu tun Wahl
jk.

3

Eine Sache, die Sprachen tun könnten: Entfernen Sie den Gleichheitsvergleich von anderen Gleitkommatypen als einem direkten Vergleich mit den NAN-Werten.

Gleichheitstests würden nur als Funktionsaufruf existieren, der die beiden Werte und ein Delta angenommen hat, oder für Sprachen wie C #, die es zulassen, dass Typen Methoden haben, die den anderen Wert und das Delta annehmen.


3

Ich finde es seltsam, dass niemand auf den rationalen Zahlentrick der Familie Lisp hingewiesen hat.

Im Ernst, öffne sbcl und mache das: (+ 1 3)und du bekommst 4. Wenn du das tust *( 3 2), bekommst du 6. Jetzt versuche (/ 5 3)und du bekommst 5/3 oder 5 Drittel.

Das sollte in manchen Situationen etwas helfen, oder?


Ich frage mich, ob es möglich ist zu wissen, ob ein Ergebnis als 1/3 dargestellt werden muss oder eine exakte Dezimalzahl sein könnte.
Mamcx

guter Vorschlag
Peter Porfy

3

Eine Sache, die ich gerne sehen würde, wäre die Erkenntnis, dass doubleto floatals eine zunehmende Konversion angesehen werden sollte, während floatto doublesich verengt (*). Das mag kontraintuitiv erscheinen, aber überlegen Sie, was die Typen tatsächlich bedeuten:

  • 0,1f bedeutet "13.421.773,5 / 134.217.728, plus oder minus 1 / 268.435.456 oder so".
  • 0,1 bedeutet wirklich 3,602,879,701,896,397 / 36,028,797,018,963,968, plus oder minus 1 / 72,057,594,037,927,936 oder so.

Wenn man ein hat, doubledas die beste Darstellung der Größe "ein Zehntel" enthält und in floatumrechnet, ist das Ergebnis "13.421.773,5 / 134.217.728, plus oder minus 1 / 268.435.456 oder so", was eine korrekte Beschreibung des Wertes ist.

Im Gegensatz floatdazu doubleist das Ergebnis "13.421.773,5 / 134.217.728, plus oder minus 1 / 72.057.594.037.927.936 oder so" - ein Grad an impliziter Genauigkeit , wenn a die beste Darstellung der Menge "ein Zehntel" enthält und in diese umwandelt das ist falsch um einen Faktor von über 53 Millionen.

Obwohl der IEEE-744-Standard verlangt, dass Gleitkomma-Berechnungen so durchgeführt werden, als ob jede Gleitkommazahl die exakte numerische Größe genau in der Mitte ihres Bereichs darstellt, sollte dies nicht bedeuten, dass die Gleitkomma-Werte tatsächlich diese exakten Werte darstellen numerische Größen. Das Erfordernis, dass die Werte in der Mitte ihrer Bereiche angenommen werden, ergibt sich vielmehr aus drei Tatsachen: (1) Berechnungen müssen so durchgeführt werden, als ob die Operanden einige bestimmte genaue Werte haben; (2) konsistente und dokumentierte Annahmen sind hilfreicher als inkonsistente oder undokumentierte Annahmen; (3) Wenn man eine konsistente Annahme machen will, kann keine andere konsistente Annahme besser sein als die Annahme, dass eine Menge das Zentrum ihres Bereichs darstellt.

Ich erinnere mich übrigens an ungefähr 25 Jahre zuvor, als jemand ein numerisches Paket für C erfand, das "Range Types" verwendete, die jeweils aus einem Paar 128-Bit-Floats bestanden. Alle Berechnungen würden so durchgeführt, dass für jedes Ergebnis der minimal und maximal mögliche Wert berechnet wird. Wenn man eine große lange iterative Berechnung durchführt und einen Wert von [12.53401391134 12.53902812673] feststellt, kann man sicher sein, dass viele Stellen der Genauigkeit durch Rundungsfehler verloren gegangen sind, das Ergebnis aber immer noch vernünftigerweise als 12,54 ausgedrückt werden kann (und es war nicht ' t wirklich 12,9 oder 53,2). Ich bin überrascht, dass ich keine Unterstützung für solche Typen in gängigen Sprachen gesehen habe, zumal sie gut zu mathematischen Einheiten passen, die mit mehreren Werten gleichzeitig arbeiten können.

(*) In der Praxis ist es oft hilfreich, Werte mit doppelter Genauigkeit zu verwenden, um Zwischenberechnungen durchzuführen, wenn mit Zahlen mit einfacher Genauigkeit gearbeitet wird. Daher kann es ärgerlich sein, für alle derartigen Vorgänge einen Typecast zu verwenden. Sprachen könnten helfen, indem sie einen "Fuzzy Double" -Typ haben, der Berechnungen als Double ausführt und frei von und nach Single umgewandelt werden kann. Dies wäre besonders hilfreich, wenn Funktionen, die Parameter vom Typ doubleund vom Rückgabewert annehmen double, markiert werden könnten, damit sie automatisch eine Überladung erzeugen, die stattdessen "Fuzzy Double" akzeptiert und zurückgibt.


2

Wenn mehr Programmiersprachen eine Seite aus Datenbanken entnehmen und es den Entwicklern ermöglichen würden, die Länge und Genauigkeit ihrer numerischen Datentypen festzulegen, könnten sie die Wahrscheinlichkeit von Gleitkommafehlern erheblich verringern. Wenn eine Sprache es einem Entwickler ermöglicht, eine Variable als Gleitkomma (2) zu deklarieren, was darauf hinweist, dass eine Gleitkommazahl mit zwei Dezimalstellen Genauigkeit erforderlich ist, können mathematische Operationen viel sicherer ausgeführt werden. In diesem Fall wird die Variable intern als Ganzzahl dargestellt und vor dem Anzeigen des Werts durch 100 dividiert. In diesem Fall kann die Geschwindigkeit durch Verwendung der schnelleren Ganzzahl-Arithmetikpfade verbessert werden. Die Semantik eines Floats (2) würde es Entwicklern auch ermöglichen, die ständige Notwendigkeit zu vermeiden, Daten vor der Ausgabe zu runden, da ein Float (2) Daten inhärent auf zwei Dezimalstellen rundet.

Natürlich müssen Sie einem Entwickler erlauben, nach einem Gleitkommawert mit maximaler Genauigkeit zu fragen, wenn der Entwickler diese Genauigkeit benötigt. Und Sie würden Probleme einführen, bei denen geringfügig unterschiedliche Ausdrücke derselben mathematischen Operation aufgrund von Zwischenrundungsoperationen möglicherweise unterschiedliche Ergebnisse liefern, wenn Entwickler ihre Variablen nicht präzise genug ausführen. Aber zumindest in der Datenbankwelt scheint das keine allzu große Sache zu sein. Die meisten Menschen führen nicht die Art von wissenschaftlichen Berechnungen durch, die eine hohe Genauigkeit der Zwischenergebnisse erfordern.


Die Angabe von Länge und Genauigkeit würde nur sehr wenig Sinn machen. Eine Festkommabasis 10 wäre nützlich für die Finanzverarbeitung, da dadurch ein Großteil der Überraschungen, die Gleitkomma-Betroffene erleben, beseitigt werden.
David Thornley

@ David - Vielleicht fehlt mir etwas, aber wie unterscheidet sich ein Festkomma-Basis-10-Datentyp von dem, was ich hier vorschlage? Ein Float (2) in meinem Beispiel hätte eine feste 2-stellige Dezimalstelle und würde automatisch auf das nächste Hundertstel gerundet, was Sie wahrscheinlich für einfache Finanzberechnungen verwenden würden. Komplexere Berechnungen würden erfordern, dass der Entwickler eine größere Anzahl von Dezimalstellen zuweist.
Justin Cave

1
Was Sie befürworten, ist ein Festkomma-Basis-10-Datentyp mit programmiererspezifischer Genauigkeit. Ich sage, dass die vom Programmierer angegebene Genauigkeit größtenteils sinnlos ist und nur zu den Fehlerarten führt, auf die ich in COBOL-Programmen gestoßen bin. (Wenn Sie beispielsweise die Genauigkeit von Variablen ändern, können Sie leicht eine Variable übersehen, durch die der Wert läuft. Bei einer anderen ist die Größe des Zwischenergebnisses wesentlich wichtiger als die richtige.)
David Thornley

4
Ein Float(2)wie du vorschlägst sollte nicht genannt werden Float, da hier nichts schwebt, schon gar nicht der "Dezimalpunkt".
Paŭlo Ebermann

1
  • Sprachen werden vom Typ Dezimal unterstützt. Natürlich löst dies das Problem nicht wirklich, dennoch haben Sie keine exakte und endliche Darstellung von zum Beispiel ⅓;
  • Einige DBs und Frameworks unterstützen den Typ Money. Dabei wird die Anzahl der Cents im Grunde genommen als Ganzzahl gespeichert.
  • Es gibt einige Bibliotheken zur Unterstützung rationaler Zahlen. das löst das Problem von ⅓, löst aber nicht das Problem von zum Beispiel √2;

Dies gilt in einigen Fällen, ist jedoch keine allgemeine Lösung für den Umgang mit Float-Werten. Die wirkliche Lösung besteht darin, das Problem zu verstehen und zu lernen, wie man damit umgeht. Wenn Sie Gleitkommaberechnungen verwenden, sollten Sie immer überprüfen, ob Ihre Algorithmen numerisch stabil sind . Es gibt ein riesiges Gebiet der Mathematik / Informatik, das sich auf das Problem bezieht. Es heißt Numerische Analyse .


1

Wie andere Antworten festgestellt haben, besteht die einzige Möglichkeit, Fließkommafälle in Finanzsoftware zu vermeiden, darin, sie dort nicht zu verwenden. Dies kann tatsächlich möglich sein - wenn Sie eine gut konzipierte Bibliothek für Finanzmathematik bereitstellen .

Funktionen zum Importieren von Gleitkommaschätzwerten sollten eindeutig als solche gekennzeichnet und mit für diese Operation geeigneten Parametern versehen sein, z.

Finance.importEstimate(float value, Finance roundingStep)

Der einzige wirkliche Weg, um Fließkomma-Fallstricke im Allgemeinen zu vermeiden, ist die Bildung - Programmierer müssen etwas lesen und verstehen, was jeder Programmierer über Fließkomma-Arithmetik wissen sollte .

Ein paar Dinge, die helfen könnten:

  • Ich werde diejenigen unterstützen, die fragen: "Warum ist eine exakte Gleichheitsprüfung für Fließkommazahlen überhaupt legal?"
  • Verwenden Sie stattdessen eine isNear()Funktion.
  • Bereitstellen und Fördern der Verwendung von Fließkomma-Akkumulatorobjekten (die Sequenzen von Fließkommawerten stabiler hinzufügen, als sie alle zu einer regulären Fließkommavariablen hinzuzufügen).

-1

Die meisten Programmierer wären überrascht, dass COBOL das richtig verstanden hat ... In der ersten Version von COBOL gab es keinen Fließkomma, nur Dezimalzahlen, und die Tradition in COBOL setzte sich bis heute fort, dass das erste, woran Sie denken, wenn Sie eine Zahl deklarieren, Dezimalzahlen sind. .. Gleitkomma würde nur verwendet, wenn Sie es wirklich brauchten. Als C kam, gab es aus irgendeinem Grund keinen primitiven Dezimaltyp. Meiner Meinung nach begannen hier alle Probleme.


1
C hatte keinen Dezimaltyp, da es nicht primitiv ist, da nur sehr wenige Computer über Hardware-Dezimalanweisungen verfügen. Man könnte sich fragen, warum BASIC und Pascal es nicht hatten, da sie nicht so konzipiert waren, dass sie sich eng an das Metall anpassten. COBOL und PL / I sind die einzigen Sprachen, die ich aus dieser Zeit kenne und die so etwas hatten.
David Thornley

3
@JoelFan: Wie schreibt man ⅓ in COBOL? Dezimalzahl löst keine Probleme, Basis 10 ist genauso ungenau wie Basis 2.
Vartec

2
Dezimal löst das Problem der exakten Darstellung von Dollar und Cent, was für eine "geschäftsorientierte" Sprache nützlich ist. Ansonsten ist Dezimalzahl nutzlos. Es hat die gleichen Arten von Fehlern (z. B. 1/3 * 3 = 0,99999999), während es viel langsamer ist. Aus diesem Grund ist dies nicht die Standardeinstellung in Sprachen, die nicht speziell für die Buchhaltung entwickelt wurden.
Dan04

1
FORTRAN, das mehr als ein Jahrzehnt vor C liegt, unterstützt auch keine Standard-Dezimalstellen.
Dan04

1
@JoelFan: Wenn Sie einen vierteljährlichen Wert haben und einen monatlichen Wert benötigen, raten Sie, was Sie tun müssen, um ihn mit ... nein, es ist nicht 0,33, es ist ⅓.
Vartec
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.