web-dev-qa-db-de.com

Verschieben von Dezimalstellen im Doppel

Also habe ich eine doppelte Menge gleich 1234, ich möchte eine Dezimalstelle verschieben, um es 12,34 zu machen

Um dies zu tun, multipliziere ich .1 bis 1234 zweimal, irgendwie so

double x = 1234;
for(int i=1;i<=2;i++)
{
  x = x*.1;
}
System.out.println(x);

Dies wird das Ergebnis "12.340000000000002" drucken

Gibt es eine Möglichkeit, ohne es einfach auf zwei Dezimalstellen zu formatieren, das Double Store 12.34 korrekt zu haben?

94
BlackCow

Wenn Sie double oder float verwenden, sollten Sie die Rundung verwenden oder mit Rundungsfehlern rechnen. Wenn Sie dies nicht tun können, verwenden Sie BigDecimal.

Das Problem, das Sie haben, ist, dass 0.1 keine exakte Darstellung ist. Wenn Sie die Berechnung zweimal ausführen, addieren Sie diesen Fehler.

Es können jedoch 100 genau dargestellt werden. Versuchen Sie Folgendes:

double x = 1234;
x /= 100;
System.out.println(x);

welche druckt:

12.34

Dies funktioniert, weil Double.toString(d) in Ihrem Namen eine kleine Rundung durchführt, aber es ist nicht viel. Wenn Sie sich fragen, wie es ohne Rundung aussehen könnte:

System.out.println(new BigDecimal(0.1));
System.out.println(new BigDecimal(x));

druckt:

0.100000000000000005551115123125782702118158340454101562
12.339999999999999857891452847979962825775146484375

Kurz gesagt, für sinnvolle Antworten in Gleitkommazahlen ist eine Rundung unvermeidlich, unabhängig davon, ob Sie dies explizit tun oder nicht.


Hinweis: x / 100 Und x * 0.01 Stimmen hinsichtlich des Rundungsfehlers nicht genau überein. Dies liegt daran, dass der Rundungsfehler für den ersten Ausdruck von den Werten von x abhängt, wohingegen 0.01 Im zweiten Ausdruck einen festen Rundungsfehler aufweist.

for(int i=0;i<200;i++) {
    double d1 = (double) i / 100;
    double d2 = i * 0.01;
    if (d1 != d2)
        System.out.println(d1 + " != "+d2);
}

druckt

0.35 != 0.35000000000000003
0.41 != 0.41000000000000003
0.47 != 0.47000000000000003
0.57 != 0.5700000000000001
0.69 != 0.6900000000000001
0.7 != 0.7000000000000001
0.82 != 0.8200000000000001
0.83 != 0.8300000000000001
0.94 != 0.9400000000000001
0.95 != 0.9500000000000001
1.13 != 1.1300000000000001
1.14 != 1.1400000000000001
1.15 != 1.1500000000000001
1.38 != 1.3800000000000001
1.39 != 1.3900000000000001
1.4 != 1.4000000000000001
1.63 != 1.6300000000000001
1.64 != 1.6400000000000001
1.65 != 1.6500000000000001
1.66 != 1.6600000000000001
1.88 != 1.8800000000000001
1.89 != 1.8900000000000001
1.9 != 1.9000000000000001
1.91 != 1.9100000000000001
185
Peter Lawrey

Nein - Wenn Sie Dezimalwerte genau speichern möchten, verwenden Sie BigDecimal. double einfach kann nicht eine Zahl wie 0.1 genau darstellen, genauso wenig wie Sie den Wert eines Drittels genau mit einer endlichen Anzahl von Dezimalstellen schreiben können.

52
Jon Skeet

wenn es sich um nur Formatierung handelt, versuchen Sie es mit printf

double x = 1234;
for(int i=1;i<=2;i++)
{
  x = x*.1;
}
System.out.printf("%.2f",x);

ausgabe

12.34
46
Augusto

In Finanzsoftware ist es üblich, ganze Zahlen für ein paar Cent zu verwenden. In der Schule wurde uns beigebracht, wie man Festkomma anstelle von Floating verwendet, aber das sind normalerweise Zweierpotenzen. Das Speichern von Pennies in ganzen Zahlen kann auch als "Festpunkt" bezeichnet werden.

int i=1234;
printf("%d.%02d\r\n",i/100,i%100);

Im Unterricht wurden wir generell gefragt, welche Zahlen in einer Basis genau dargestellt werden können.

Zum base=p1^n1*p2^n2... Sie können jedes N darstellen, wobei N = n * p1 ^ m1 * p2 ^ m2.

Lassen base=14=2^1*7^1... Sie können 1/7 1/14 1/28 1/49 aber nicht 1/3 darstellen

Ich kenne mich mit Finanzsoftware aus - ich habe Ticketmasters Finanzberichte von VAX asm auf Pascal konvertiert. Sie hatten ein eigenes Format () mit Codes für Pennies. Der Grund für die Konvertierung war, dass 32-Bit-Ganzzahlen nicht mehr ausreichten. +/- 2 Milliarden Pennies sind 20 Millionen US-Dollar und das ist für die Weltmeisterschaft oder die Olympischen Spiele übergelaufen, habe ich vergessen.

Ich wurde zur Geheimhaltung verpflichtet. Naja. Im akademischen Bereich veröffentlichen Sie, wenn es gut ist; In der Industrie hält man es geheim.

26

sie können die Darstellung von Ganzzahlen versuchen

int i =1234;
int q = i /100;
int r = i % 100;

System.out.printf("%d.%02d",q, r);
12
Angel Koh

Dies wird durch die Art und Weise verursacht, in der Computer Gleitkommazahlen speichern. Sie tun das nicht genau. Als Programmierer sollten Sie diese Gleitkomma-Anleitung lesen, um sich mit den Schwierigkeiten beim Umgang mit Gleitkommazahlen vertraut zu machen.

10
CanSpice

Witzig, dass in zahlreichen Beiträgen die Verwendung von BigDecimal erwähnt wird, aber niemand die Frage beantwortet, ob die richtige Antwort auf BigDecimal basiert. Denn selbst mit BigDecimal können Sie immer noch Fehler machen, wie dieser Code zeigt

String numstr = "1234";
System.out.println(new BigDecimal(numstr).movePointLeft(2));
System.out.println(new BigDecimal(numstr).multiply(new BigDecimal(0.01)));
System.out.println(new BigDecimal(numstr).multiply(new BigDecimal("0.01")));

Gibt diese Ausgabe aus

12.34
12.34000000000000025687785232264559454051777720451354980468750
12.34

Der BigDecimal-Konstruktor erwähnt ausdrücklich, dass es besser ist, den String-Konstruktor als einen numerischen Konstruktor zu verwenden. Höchste Präzision wird auch durch den optionalen MathContext beeinflusst.

Gemäß dem BigDecimal-Javadoc es ist möglich ein BigDecimal zu erstellen, das genau gleich 0.1 ist, vorausgesetzt, Sie verwenden den String-Konstruktor .

9
Justin Rowe

Ja da ist. Bei jeder Doppeloperation kann es zu Genauigkeitsverlusten kommen, aber die Genauigkeit ist für jede Operation unterschiedlich und kann durch Auswahl der richtigen Abfolge von Operationen minimiert werden. Wenn Sie beispielsweise eine Menge von Zahlen multiplizieren, ist es am besten, die Mengen vor dem Multiplizieren nach Exponenten zu sortieren.

Jedes anständige Buch über Zahlenkalkulation beschreibt dies. Zum Beispiel: http://docs.Oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Und um Ihre Frage zu beantworten:

Verwenden Sie dividieren statt multiplizieren, um ein korrektes Ergebnis zu erhalten.

double x = 1234;
for(int i=1;i<=2;i++)
{
  x =  x / 10.0;
}
System.out.println(x);
5
Jan Kotek

Nein, da Java-Gleitkommatypen (in der Tat alle Gleitkommatypen) ein Kompromiss zwischen Größe und Genauigkeit sind. Während sie für viele Aufgaben sehr nützlich sind, sollten Sie BigDecimal verwenden, wenn Sie eine beliebige Genauigkeit benötigen.

3
biziclop