Fließkomma-Arithmetik liefert keine exakten Ergebnisse [duplicate]

Lesezeit: 7 Minuten

Ich muss in Java Gleitkommaarithmetik durchführen, wie im folgenden Code gezeigt:

public class TestMain {
    private static Map<Integer, Double> ccc = new HashMap<Integer, Double>() {
      { put(1, 0.01); put(2, 0.02); put(3, 0.05); put(4, 0.1); put(6, 0.2);
        put(10, 0.5); put(20, 1.0); put(30, 2.0); put(50, 5.0); put(100, 10.0);
      }
    };

    Double increment(Double i, boolean up) {
        Double inc = null;

        while (inc == null) {
            inc = ccc.get(i.intValue());

            if (up)
                --i;
            else
                ++i;
        }
        return inc;
    }

    public static void main(String[] args) {
        TestMain tt = new TestMain();

        for (double i = 1; i < 1000; i += tt.increment(i, true)) {
            System.out.print(i + ",");
        }
    }
}

Dies dient dazu, den Wertebereich zu simulieren, der als Ausgabe von angegeben wird Betfair Spinner-Widget.

Fließkomma-Arithmetik in Java scheint einige unerwartete Fehler einzuführen. Ich bekomme zum Beispiel 2,180000000000001 statt 2,18. Was nützen Fließkommazahlen, wenn Sie den Ergebnissen der mit ihnen durchgeführten Arithmetik nicht vertrauen können? Wie kann ich dieses Problem umgehen?

  • Willkommen in der Informatik. 🙂

    – Bobby Shaftoe

    2. November 2009 um 13:25 Uhr

  • Sehen Sie sich diese Frage an, die zwar unterschiedlich formuliert ist, aber zur gleichen Antwort führt. stackoverflow.com/questions/1088216/…

    – Yishai

    2. November 2009 um 14:09 Uhr

  • Die Frage kann umformuliert werden als: Ungenaue Arithmetik, die keine exakten Werte liefert. Sie wetten!

    – Ing

    2. Februar 2013 um 12:12 Uhr

  • Um nicht unfair zu sein, aber Programmierer sollten sich wie in jedem ernsthaften Informatikkurs ein wenig mit der Typdarstellung befassen. In meiner Universität geht es in der ersten Prüfung darum, IEEE-Nummern manuell zu berechnen … 🙂

    – ingconti

    16. August 2017 um 10:10 Uhr


Flieskomma Arithmetik liefert keine exakten Ergebnisse duplicate
Jon Skeet

Wenn Sie genau brauchen Dezimal Werte, die Sie verwenden sollten java.math.BigDecimal. Dann lies “Was jeder Informatiker über Gleitkommaarithmetik wissen sollte” für den Hintergrund, warum Sie diese Ergebnisse erhalten.

(Ich habe ein .NET-zentrierter Artikel die Sie möglicherweise einfacher zu lesen finden – und sicherlich kürzer. Die Unterschiede zwischen Java und .NET sind für das Verständnis dieses Problems größtenteils irrelevant.)

  • Merken Sie sich, BigDecimal wird viel langsamer sein als einfache Gleitkommaarithmetik.

    – Anthony Mühlen

    2. November 2009 um 13:27 Uhr

  • @Anthony: Stimmt, aber wenn du es tatsächlich bist brauchen Genaue Dezimalwerte, dann ist langsam und richtig besser als schnell und falsch.

    – Jon Skeet

    2. November 2009 um 13:47 Uhr

  • Sie können in diesem Fall auch ganze Zahlen für Cent verwenden. Sie müssen sich daran erinnern, dass 218 2,18 bedeutet, und Sie müssen etwas zusätzliche Arbeit zum Drucken leisten.

    – sternenblau

    2. November 2009 um 14:35 Uhr

  • Ich liebe Mathe, und selbst ich kann diesen Artikel nicht beenden. Ich wünschte, die Leute würden aufhören, es zu posten – es ist keine gute Referenz, es sei denn, Sie möchten ein kleines Buch lesen, nur um zu verstehen, wie Gleitkommazahlen funktionieren.

    – BlueRaja – Danny Pflughöft

    8. April 2011 um 19:52 Uhr

  • Floating-Point-GUI.de hat einen etwas einfacheren und praktischeren Ansatz, um das/die Problem(e) zu erklären.

    – Joachim Sauer

    20. März 2013 um 13:21 Uhr

Fließkommazahlen verwenden binäre Brüche und keine Dezimalbrüche. Das heißt, Sie sind an Dezimalbrüche gewöhnt, die aus einer Zehntelziffer, einer Hundertstelziffer, einer Tausendstelziffer usw. bestehen. d1/10 + d2/100 + d3/1000 … Aber Gleitkommazahlen sind also binär sie haben eine Halbziffer, eine Viertelziffer, eine Achtelziffer usw. d1/2 + d2/4 + d3/8 …

Viele Dezimalbrüche können nicht genau in einer endlichen Anzahl von Binärziffern ausgedrückt werden. 1/2 ist zum Beispiel kein Problem: Dezimal ist es 0,5, binär 0,1. 3/4 ist dezimal 0,75, binär 0,11. Aber 1/10 ist dezimal eine saubere 0,1, aber binär ist es 0,0001100110011 … wobei sich „0011“ für immer wiederholt. Da der Computer nur eine endliche Anzahl von Ziffern speichern kann, muss diese irgendwann abgeschnitten werden, sodass die Antwort nicht genau ist. Wenn wir bei der Ausgabe wieder in Dezimalzahl umwandeln, erhalten wir eine seltsam aussehende Zahl.

Wie Jon Skeet sagt, verwenden Sie BigDecimal, wenn Sie exakte Dezimalbrüche benötigen. Wenn die Leistung ein Problem ist, können Sie Ihre eigenen Dezimalbrüche würfeln. Wenn Sie beispielsweise wissen, dass Sie immer genau 3 Dezimalstellen wollen und dass die Zahlen nicht mehr als eine Million oder so betragen, können Sie einfach ints mit angenommenen 3 Dezimalstellen verwenden und bei Bedarf Anpassungen vornehmen, wenn Sie rechnen und eine Ausgabe schreiben Format-Funktion, um den Dezimalpunkt an der richtigen Stelle einzufügen. Aber in 99 % der Fälle ist die Leistung nicht groß genug, um die Mühe wert zu sein.

Gleitkommazahlen sind ungenau, zumal sie mit binären Brüchen (1/2, 1/4, 1/8, 1/16, 1/32, …) statt mit Dezimalbrüchen (1/10, 1/ 100, 1/1000, …). Definieren Sie einfach, was Ihrer Meinung nach “nahe genug” ist, und verwenden Sie so etwas wie Math.abs(a-b) < 0.000001.

Flieskomma Arithmetik liefert keine exakten Ergebnisse duplicate
Jay

Aus philosophischer Sicht frage ich mich: Die meisten Computer-CPUs haben heute eingebaute Unterstützung für Integer-Arithmetik und Fließkomma-Arithmetik, aber keine Unterstützung für Dezimal-Arithmetik. Warum nicht? Ich habe seit Jahren keine Anwendung mehr geschrieben, in der Floats wegen dieses Rundungsproblems verwendbar waren. Sie können sie sicherlich nicht für Geldbeträge verwenden: Niemand möchte einen Preis von “42,3200003 $” auf einen Kassenbon drucken. Kein Buchhalter wird akzeptieren, dass „wir hier und da um einen Cent danebenliegen, weil wir binäre Brüche verwenden und Rundungsfehler hatten“.

Schwimmer eignen sich gut für Messungen wie Entfernung oder Temperatur, bei denen es keine “exakte Antwort” gibt und Sie irgendwann sowieso auf die Genauigkeit Ihrer Instrumente abrunden müssen. Ich nehme an, für Leute, die den Computer im Chemielabor programmieren, werden Schwimmer routinemäßig verwendet. Aber für diejenigen von uns in der Geschäftswelt sind sie ziemlich nutzlos.

Damals, als ich noch auf Mainframes programmierte, hatte die IBM 360-CPU-Familie eingebaute Unterstützung für gepackte Dezimalarithmetik. Sie speicherten Zeichenfolgen, bei denen jedes Byte zwei Dezimalziffern enthielt, dh die ersten vier Bits hatten Werte von 0 bis 9 und die zweiten vier Bits ebenso, und die CPU hatte arithmetische Funktionen, um sie zu manipulieren. Warum kann Intel so etwas nicht? Dann könnte Java einen “dezimalen” Datentyp hinzufügen und wir würden den ganzen zusätzlichen Müll nicht brauchen.

Ich sage natürlich nicht, Floats abzuschaffen. Fügen Sie einfach Dezimalstellen hinzu.

Na ja, was große soziale Bewegungen angeht, denke ich nicht, dass dies eine Menge Volksaufregung oder Unruhen auf den Straßen hervorrufen wird.

Sie können die Ausgabe Ihres Programms so gestalten, wie Sie es erwarten, indem Sie eine formatierte Ausgabe verwenden.

http://java.sun.com/javase/6/docs/api/java/util/Formatter.html

Offensichtlich funktioniert die zugrunde liegende Gleitkomma-Arithmetik immer noch gleich, aber zumindest wird die Ausgabe besser lesbar sein.

Zum Beispiel, um Ihre Ergebnisse auf zwei Dezimalstellen zu runden:

System.out.print(String.format(".2f", i) + ","); 

Flieskomma Arithmetik liefert keine exakten Ergebnisse duplicate
don_q

Sie können Code schreiben, um Epsilon auf Ihrem Computer zu berechnen. Ich glaube, std:: C++ definiert es, es ist auf andere Weise definiert, je nachdem, was Sie verwenden.

private static float calcEpsilonFloat() {
    float epsi = 1.0f;


    while ((float) (1.0 + (epsi / 2.0)) != 1.0)
    {
       epsi /= 2.0f;
    }

    return epsi;
}

Das einzige Mal, dass ich mir Sorgen um Epsilon machte, war, als ich Signale für die Schwellenwertbildung verglich. Selbst dann bin ich mir nicht sicher, ob ich mir meiner Erfahrung nach wirklich Sorgen machen musste, wenn Sie sich Sorgen um Epsilon machen Du darfst haben Sie einige andere Überlegungen, mit denen Sie sich zuerst befassen müssen.

1646643432 25 Flieskomma Arithmetik liefert keine exakten Ergebnisse duplicate
cr0

Übrigens können Sie versuchen, diese Funktion zu verwenden, um sicherzustellen (für nicht zu viele Dezimalstellen), dass Ihre Nummer neu formatiert wird, um nur die von Ihnen benötigten Dezimalstellen beizubehalten.

http://pastebin.com/CACER0xK

n ist Ihre Zahl mit vielen Dezimalstellen (zB Math.PI),
numberOfDecimals ist die maximale Anzahl an Dezimalstellen, die Sie benötigen (z. B. 2 für 3,14 oder 3 für 3,151).

Theoretisch wird ein negativer Wert angesetzt numberOfDecmals, werden auch die unteren ganzzahligen Stellen der Zahl abgeschnitten. zB Putten n=1588.22 und numberOfDecimals=-2kehrt die Funktion zurück 1500.0.

Lassen Sie mich wissen, wenn etwas nicht stimmt.

  • Funktioniert nicht und der Code hätte hier gepostet werden sollen.

    – Benutzer207421

    27. Januar 2015 um 3:38 Uhr

964580cookie-checkFließkomma-Arithmetik liefert keine exakten Ergebnisse [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy