Frage

Zur Zeit habe ich diese Methode:

static boolean checkDecimalPlaces(double d, int decimalPlaces){
    if (d==0) return true;

    double multiplier = Math.pow(10, decimalPlaces); 
    double check  =  d * multiplier;
    check = Math.round(check);      
    check = check/multiplier; 
    return (d==check);      
}

Aber diese Methode nicht für checkDecmialPlaces(649632196443.4279, 4) wahrscheinlich, weil ich auf einer Basis 2 Nummer Basis 10 Mathematik zu tun.

Wie kann diese Überprüfung richtig gemacht werden?

Ich dachte, eine String-Darstellung des doppelten Wert des Erhaltens und dann, dass der Check mit einem regexp - aber das fühlte sich seltsam

.

EDIT: Vielen Dank für alle Antworten. Es gibt Fälle, in denen ich wirklich eine doppelte und für jene Fälle, die ich umgesetzt folgendes:

private static boolean checkDecimalPlaces(double d, int decimalPlaces) {
    if (d == 0) return true;

    final double epsilon = Math.pow(10.0, ((decimalPlaces + 1) * -1));

    double multiplier = Math.pow(10, decimalPlaces);
    double check = d * multiplier;
    long checkLong = (long) Math.abs(check);
    check = checkLong / multiplier;

    double e = Math.abs(d - check);
    return e < epsilon;
}

änderte ich den round zu einer Verkürzung. Es scheint, dass die in round getan Berechnung zu viel die Ungenauigkeit erhöht. Zumindest in dem fehlerhaften Testfall.
Wie einige von dir wiesen darauf hin, wenn ich auf den ‚echten‘ String-Input bekommen könnte, wenn ich BigDecimal verwenden, um zu überprüfen und so habe ich getan:

BigDecimal decimal = new BigDecimal(value);
BigDecimal checkDecimal = decimal.movePointRight(decimalPlaces);
return checkDecimal.scale() == 0;

Der double Wert I kommt von der Apache POI API erhalten, die Excel-Dateien liest. Ich habe ein paar Tests und fand heraus, dass, obwohl die API double Werte für numerische Zellen zurückgibt, kann ich eine genaue Darstellung bekommen, wenn ich sofort, dass double Format mit dem DecimalFormat:

DecimalFormat decimalFormat = new DecimalFormat();
decimalFormat.setMaximumIntegerDigits(Integer.MAX_VALUE);
// don't use grouping for numeric-type cells
decimalFormat.setGroupingUsed(false);
decimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
value = decimalFormat.format(numericValue);

Das funktioniert auch für Werte, die nicht gerade im Binärformat dargestellt werden können.

War es hilfreich?

Lösung

Der Test schlägt fehl, da Sie die Genauigkeit der binären Fließpunktdarstellung erreicht haben, das mit etwa 16 Stellen ist http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

Eine bessere Art und Weise, ob die n+1 Dezimalstellen unter einer bestimmten Schwelle zu überprüfen sei. Wenn d - round(d) weniger als epsilon (siehe Grenze ), hat die Dezimaldarstellung d keine signifikanten Dezimalstellen. Ebenso, wenn (d - round(d)) * 10^n weniger als epsilon ist, kann d hat höchstens signifikante Stellen n.

Verwenden Sie Jon Skeet 's

Andere Tipps

Wenn Ihr Ziel ist es, eine Zahl mit genau darzustellen n signifikanten Zahlen rechts von der Dezimalzahl, BigDecimal ist die Klasse zu verwenden.

  

Immutable, beliebige Genauigkeit unterzeichnet   Dezimal Zahlen. A BigDecimal besteht   mit beliebiger Genauigkeit integer   unskalierten Wert und ein 32-Bit-Integer   Rahmen. Wenn Null oder positiv, die Skala   ist die Anzahl der Ziffern rechts   von dem Komma. Wenn negativ, die   unskalierten Wert der Zahl ist,   um zehn bis die volle Leistung der multiplizierten   Negation der Skala. Der Wert von   die Zahl durch die dargestellte   BigDecimal ist daher (unscaledValue   × 10-Skala).

scale kann über setScale (int)

Wie bei allen Gleitkomma-Arithmetik, sollten Sie nicht auf Gleichheit überprüfen, sondern vielmehr, dass der Fehler (epsilon) ausreichend klein ist.

Wenn Sie ersetzen:

return (d==check);

mit so etwas wie

return (Math.abs(d-check) <= 0.0000001);

sollte es funktionieren. Offensichtlich soll das epsilon ausgewählt werden, dass sie klein genug, um im Vergleich zu der Anzahl der Dezimalstellen für Sie überprüfen.

Der double Typ ist eine binäre Gleitpunktzahl. Es gibt immer offensichtlich Ungenauigkeiten im Umgang mit ihnen, als ob sie dezimal Gleitkommazahlen wurden. Ich weiß nicht, dass Sie jemals in der Lage sein, Ihre Funktion zu schreiben, so dass es die Art und Weise funktioniert, wie Sie wollen.

Sie werden wahrscheinlich wieder auf die ursprüngliche Quelle der Nummer gehen (ein String-Eingang vielleicht) und die Dezimaldarstellung zu halten, wenn es für Sie wichtig ist.

Wenn Sie auf BigDecimal wechseln, dann als Ken G erklärt, das ist, was Sie verwenden sollen.

Wenn nicht, dann haben Sie mit einer Vielzahl von Fragen zu befassen, wie in den anderen Antworten erwähnt. Für mich beschäftigen Sie mit einer binären Zahl (double) und eine Frage zu einer Dezimaldarstellung dieser Zahl zu fragen; das heißt, fragen Sie einen String. Ich denke, Ihre Intuition richtig ist.

Ich bin nicht sicher, dass dies in der Regel wirklich machbar ist. Zum Beispiel, wie viele Dezimalstellen hat 1.0e-13 haben? Was passiert, wenn es von einem Rundungsfehler in Folge, während Arithmetik zu tun und wirklich nur in der Verkleidung 0 ist? Wenn aktiviert, sind die andere Hand, die Sie fragen, wenn es irgendwelche Nicht-Null-Ziffern in den ersten n Dezimalstellen Sie so etwas wie tun:

   static boolean checkDecimalPlaces(double d, unsigned int decimalPlaces){
      // take advantage of truncation, may need to use BigInt here
      // depending on your range
      double d_abs = Math.abs(d);
      unsigned long d_i = d_abs; 
      unsigned long e = (d_abs - d_i) * Math.pow(10, decimalPlaces);
      return e > 0;
   }

Ich denke, das ist besser Konvertieren in String und befragen Sie den Wert für die Exponenten

 public int calcBase10Exponet (Number increment)
 {
  //toSting of 0.0=0.0
  //toSting of 1.0=1.0
  //toSting of 10.0=10.0
  //toSting of 100.0=100.0
  //toSting of 1000.0=1000.0
  //toSting of 10000.0=10000.0
  //toSting of 100000.0=100000.0
  //toSting of 1000000.0=1000000.0
  //toSting of 1.0E7=1.0E7
  //toSting of 1.0E8=1.0E8
  //toSting of 1.0E9=1.0E9
  //toSting of 1.0E10=1.0E10
  //toSting of 1.0E11=1.0E11
  //toSting of 0.1=0.1
  //toSting of 0.01=0.01
  //toSting of 0.0010=0.0010  <== need to trim off this extra zero
  //toSting of 1.0E-4=1.0E-4
  //toSting of 1.0E-5=1.0E-5
  //toSting of 1.0E-6=1.0E-6
  //toSting of 1.0E-7=1.0E-7
  //toSting of 1.0E-8=1.0E-8
  //toSting of 1.0E-9=1.0E-9
  //toSting of 1.0E-10=1.0E-10
  //toSting of 1.0E-11=1.0E-11
  double dbl = increment.doubleValue ();
  String str = Double.toString (dbl);
//  System.out.println ("NumberBoxDefaultPatternCalculator: toSting of " + dbl + "=" + str);
  if (str.contains ("E"))
  {
   return Integer.parseInt (str.substring (str.indexOf ("E") + 1));
  }
  if (str.endsWith (".0"))
  {
   return str.length () - 3;
  }
  while (str.endsWith ("0"))
  {
   str = str.substring (0, str.length () - 1);
  }
  return - (str.length () - str.indexOf (".") - 1);
 }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top