Wie kann man überprüfen, ob eine Doppel höchstens n Dezimalstellen hat?
-
06-07-2019 - |
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.
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);
}