2014-12-19 15 views
6

Ich habe eine Frage in Bezug auf Gleitkommaarithmetik in Java und seine Genauigkeit. Ich habe meine Forschung hier und über Google gemacht und bin auf einige Lösungen gestoßen, habe aber Schwierigkeiten, sie in meinem Design umzusetzen. Also verwende ich in Java die BigDecimal-Klasse, um meine Berechnungen zu präzisieren. Beachten Sie, dass die Variablen doppelt sind und die Werte bei Berechnungen eine Genauigkeit von bis zu 8 Dezimalstellen auf der rechten Seite haben können. Das Ergebnis (Genauigkeit), das angezeigt werden soll, ist bekannt, und das ist, was ich als den aktuellen Wert speichern werde. Außerdem kommen alle Werte dynamisch (über eine Methode). Das übergebene Argument sollte der currentValue + die Schrittgröße sein.Java Floating Point Precision Ausgabe

public void newValue(float value) { 

    //Clip to valid range, can't go over minimum/max value 
    value = Math.max(minimumValue, Math.min(maximumValue, value)); 

    // TODO Implement better Floating Point Arithmetic precision 

    MathContext mcI = new MathContext(0, RoundingMode.HALF_UP);  
    MathContext mcF = new MathContext(8, RoundingMode.HALF_UP); 
    BigDecimal valueBD = new BigDecimal(value, mcF); 
    BigDecimal minimumBD = new BigDecimal(minimumValue, mcF); 
    BigDecimal stepBD = new BigDecimal(step, mcF); 
    BigDecimal currentValueBD = new BigDecimal(currentValue, mcF); 


    BigDecimal totalStepsBD = valueBD.subtract(minimumBD, mcF); 

    //Ensure value is divisible by stepsize 
    totalStepsBD = totalStepsBD.divide(stepBD, mcI); 

    valueBD = stepBD.multiply(totalStepsBD, mcF); 

    valueBD = valueBD.add(minimumBD, mcF); 

    // arithmetic without using BigDecimal (old) 
    //int totalSteps = (int) ((value- minimumValue)/ step); 
    //value = totalSteps * step + minimumValue; 

    if(!(valueBD.equals(currentValueBD))) { 
    valueBD = valueBD.setScale(displayPrecision, RoundingMode.HALF_UP); 
    currentValue = valueBD.floatValue(); 
    dispatch(); 
    } 

}

Nun, es funktioniert mit einigen Werten aber nicht alle. Vor allem, wenn ich mit der Schrittweite herumhaue. Also, wenn Schritt = 0,1 war es in Ordnung. Wenn ich es 0,005 gemacht, würde ich einen AirthmeticException bekommen - nicht Dezimalzahl Expansion auf dem Schritt, in dem

totalStepsBD = totalStepsBD.divide(stepBD, mcI); 

Wenn .005 für den Schritt Variable gesetzt ist, nach einer BigDeciaml (stepBD) es kommt zu. 0049999999 ... Nicht sicher, ob das hilft, aber wenn Sie irgendwelche Ideen haben, lassen Sie es mich wissen. Vielen Dank.

+0

Ich sehe nicht ein Sie definieren 'step' überall, so dass ich nehme an, es ist eine Membervariable die Klasse. Wenn es sich um einen Float-Typ handelt und 0,005 zugewiesen wird, wird es als .0049999999 oder ähnlich gespeichert. Wenn Sie dann "stepBD" den Wert von "step" zuweisen, verwendet es die 0,00499 Werte, nicht 0,005. Nicht sicher, ob dies das Problem ist, aber es könnte helfen. – Grice

+3

Hinweis: Verwenden Sie '.equals()' nicht, um 'BigDecimal's zu vergleichen; zum Beispiel sind "0" und "0,00" nicht gleich. Verwenden Sie '.compareTo()' und markieren Sie stattdessen '== 0'. – fge

Antwort

1

Übergeben Sie einen String step (und einen String value) an den BigDecimal Konstruktor. Sie können 0.005 nicht als double (oder float) darstellen.

BigDecimal stepBD = new BigDecimal("0.005"); // <-- works as a `String`. 

bearbeiten

Oder wie unten erwähnt, verwenden BigDecimal.valueOf(double)

BigDecimal stepBD = BigDecimal.valueOf(0.005); 
+0

Ich denke, es ist besser, ['BigDecimal.valueOf (double)'] zu verwenden (http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#valueOf%28double%29), so muss OP seinen Code nicht so sehr ändern. – Tom

+1

Nur eine Anmerkung hier, BigDecimal.valueOf hat nicht für mich gearbeitet. Eher musste ich zu einer Saite greifen, wie Sie es in der ersten Lösung gezeigt haben. Vielen Dank für deine Hilfe!! –

Verwandte Themen