2010-02-19 11 views
10

ero interessato ad avere la seguente funzione getNumberOfDecimalPlace:determinare il numero di decimali Place utilizzando BigDecimal

System.out.println("0 = " + Utils.getNumberOfDecimalPlace(0));   // 0 
    System.out.println("1.0 = " + Utils.getNumberOfDecimalPlace(1.0));  // 0 
    System.out.println("1.01 = " + Utils.getNumberOfDecimalPlace(1.01)); // 2 
    System.out.println("1.012 = " + Utils.getNumberOfDecimalPlace(1.012)); // 3 
    System.out.println("0.01 = " + Utils.getNumberOfDecimalPlace(0.01)); // 2 
    System.out.println("0.012 = " + Utils.getNumberOfDecimalPlace(0.012)); // 3 

Posso sapere come posso implementare getNumberOfDecimalPlace, utilizzando BigDecimal?

Il seguente codice non funziona come previsto:

public static int getNumberOfDecimalPlace(double value) { 
    final BigDecimal bigDecimal = new BigDecimal("" + value); 
    final String s = bigDecimal.toPlainString(); 
    System.out.println(s); 
    final int index = s.indexOf('.'); 
    if (index < 0) { 
     return 0; 
    } 
    return s.length() - 1 - index; 
} 

Di seguito vengono stampati:

0.0 
0 = 1 
1.0 
1.0 = 1 
1.01 
1.01 = 2 
1.012 
1.012 = 3 
0.01 
0.01 = 2 
0.012 
0.012 = 3 

Tuttavia, per il caso del 0, 1.0, non funziona bene. Mi aspetto, "0" come risultato. Ma si sono rivelati "0.0" e "1.0". Ciò restituirà "1" come risultato.

+1

Stai pianificando che il tuo parametro di input sia BigDecimal o solo internamente per utilizzare BigDecimal? Causa nel tuo esempio di codice, il parametro di input sarebbe solo un doppio. –

+0

Ho trovato le soluzioni qui utili per l'implementazione della funzione: http://stackoverflow.com/questions/6264576/number-of-decimal-digits-in-a-double – JackDev

+0

Which "Utils" è questo? –

risposta

27

Questo codice:

int getNumberOfDecimalPlaces(BigDecimal bigDecimal) { 
    String string = bigDecimal.stripTrailingZeros().toPlainString(); 
    int index = string.indexOf("."); 
    return index < 0 ? 0 : string.length() - index - 1; 
} 

... supera questi test:

assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.001")), equalTo(3)); 
assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.01")), equalTo(2)); 
assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.1")), equalTo(1)); 
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.000")), equalTo(0)); 
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.00")), equalTo(0)); 
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.0")), equalTo(0)); 
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1")), equalTo(0)); 
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10")), equalTo(0)); 
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.1")), equalTo(1)); 
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.01")), equalTo(2)); 
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.001")), equalTo(3)); 

... se è davvero quello che vuoi. Le altre risposte sono corrette, devi utilizzare BigDecimal fino in fondo anziché doppio/float.

+1

Questa è l'unica risposta corretta sulla pagina. Perché? Perché @Robert usa stripTrailingZeros() per distinguere quei posti decimali che sono veramente significativi. Ben fatto, Robert. –

+2

@Robert Atkins ** warning **, mi dà 1 con "0.0", dovrebbe essere 0. –

+0

puoi restituire 0 quando 'scale() <= 0' o' signum() == 0', puoi anche evitare alcuni toString() - cosa così. –

0

Che ne dici di dare un'occhiata al javadoc di BigDecimal. Non sono sicuro, ma darei una prova a getScale e getPercision.

0

Il modo migliore per ottenere un BigDecimal con un numero specificato di posizioni decimali consiste nell'utilizzare il metodo setscale su di esso. Personalmente mi piace utilizzare anche la versione arrotondamento del metodo (vedi link in basso): http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html#setScale(int,%20int)

Se hai intenzione di ottenere il numero di posizioni decimali che un BigDecimal è attualmente fissato a chiamata scala associato () metodo.

+0

Non mi interessa ottenere "un BigDecimal con un numero specificato di posizioni decimali". Mi interessa sapere "un doppio con QUANTO numero di cifre decimali" –

+0

Ah .... questo è più di un problema. Il problema è che un doppio è impreciso. Ad esempio, è possibile impostare il valore di un doppio su 1,25, ma quando lo si guarda in seguito il valore restituisce come 1.24999999. Questo è dovuto al modo in cui Java lo memorizza. Questo è il motivo per cui evito il doppio il più possibile. –

+0

la descrizione è corretta, l'esempio non è così buono. 1.25 * può * essere rappresentato esattamente in un 'doppio». 1.2 per esempio * non può *. –

-6

Perché non basta modificare il codice per ottenere cifre decimali doppie?

public static int getNumberOfDecimalPlace(double value) { 
    //For whole numbers like 0 
    if (Math.round(value) == value) return 0; 
    final String s = Double.toString(value); 
    System.out.println(s); 
    final int index = s.indexOf('.'); 
    if (index < 0) { 
     return 0; 
    } 
    return s.length() - 1 - index; 
} 
+0

Non funziona. Se passi 0, verrà stampato come 0.0 –

+0

quindi fare un caso speciale per 0 –

+0

Caso speciale aggiunto :-) – Clinton

3

Non è il tuo codice che è sbagliato, ma le tue aspettative. double si basa su una rappresentazione in virgola mobile e completamente non adatta per rappresentare con precisione le frazioni decimali. Decimale 0,1 ad es. ha un numero infinito di cifre quando rappresentato in binario, quindi viene troncato e quando viene convertito in decimale, si ottengono degli errori nelle cifre meno significative.

Se si utilizza BigDecimal esclusivamente, il codice funzionerà come previsto.

+0

"Se si utilizza esclusivamente BigDecimal, il codice funzionerà come previsto." ->? Ma non funziona per il mio caso. –

+0

@Yan: non stai usando BigDecimal in esclusiva. Non devi usare valori 'double' o' float' ** compresi ** eventuali letterali di quei tipi. –

+2

@Yan: Non funziona perché si usa il doppio come tipo di parametro di input. Bisogna evitare l'uso del doppio completamente, perché nel secondo si mette il valore decimale in un doppio (anche come risultato intermedio), lo si è già corrotto. –

3

Se davvero ottieni il doppio, ti consiglio di formularli prima come stringhe prima di creare lo BigDecimal. Almeno che ha funzionato per me: How to check if a double has at most n decimal places?

seconda del numero di cifre che ci si aspetta è possibile utilizzare la formattazione standard come

String.valueOf(doubleValue); 

o si potrebbe utilizzare la formattazione specializzato per evitare formato esponenziale

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); 

Se hai un BigDecimal puoi semplicemente chiamare scale() per ottenere il numero di posizioni decimali.

+0

+1 per la scala() menzionare – chburd

+2

Il problema con scale() è che non riesce a distinguere tra posizioni decimali e cifre decimali significative. Restituisce "1" per "1.0" quando deve restituire 0, secondo la richiesta dell'OP. –

-1

La risposta di Michael Borgwardt è corretta. Non appena si usa un doppio o un float, i valori sono già corrotti.

Per fornire un esempio di codice:

System.out.println("0 = " + BigDecimalUtil.getNumberOfDecimalPlace("0")); // 0 
System.out.println("1.0 = " + BigDecimalUtil.getNumberOfDecimalPlace("1.0")); // 0 
System.out.println("1.01 = " + BigDecimalUtil.getNumberOfDecimalPlace(new BigDecimal("1.01"))); // 2 
System.out.println("1.012 = " + BigDecimalUtil.getNumberOfDecimalPlace(new BigDecimal("1.012"))); // 3 
System.out.println("0.01 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.01")); // 2 
System.out.println("0.012 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.012")); // 3 
System.out.println("0.00000000000000000012 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.00000000000000000012")); // 20 

E una versione di overload di getNumberOfDecimalPlace così si potrebbe usare con BigDecimal o stringa:

public static int getNumberOfDecimalPlace(String value) { 
    final int index = value.indexOf('.'); 
    if (index < 0) { 
     return 0; 
    } 
    return value.length() - 1 - index; 
} 

public static int getNumberOfDecimalPlace(BigDecimal value) { 
    return getNumberOfDecimalPlace(value.toPlainString()); 
} 
+0

Sei sicuro che BigDecimalUtil.getNumberOfDecimalPlace ("1.0")) tornerà a 0? –

+0

Il problema con questa risposta è che non riesce a distinguere tra posizioni decimali e cifre decimali significative. Come giustamente sottolineato @Cheok, restituisce "1" per "1.0" quando deve restituire 0, secondo la richiesta dell'OP. –

2

Prova questo:

Math.floor(Math.log(x)/Math.log(10)) 
0.001 = -3 
0.01 = -2 
0.1 = -1 
1  = 0 
10 = 1 
100 = 2 
+0

Ciò è utile per ottenere la posizione del primo numero significativo nel valore. NB: Ma per valori come 1.124 e voglio ottenere il numero di cifre decimali, non funziona. – JackDev

1

che dovrebbe farlo

int getNumberOfDecimalPlace(BigDecimal number) { 
    int scale = number.stripTrailingZeros().scale(); 
    return scale > 0 ? scale : 0; 
} 
18

Combinando Turismo, Robert e user1777653's risposte, abbiamo:

int getNumberOfDecimalPlaces(BigDecimal bigDecimal) { 
    return Math.max(0, bigDecimal.stripTrailingZeros().scale()); 
} 
  • stripTrailingZeros() assicura che il trailing gli zeri non vengono contati (ad es. 1.0 ha 0 posizioni decimali).
  • scale() è più efficiente di String.indexOf().
  • Un valore negativo scale() rappresenta zero cifre decimali.

Ecco, il meglio di entrambi i mondi.

+0

Questo potrebbe essere sbagliato, perché '10' diventa' 1E1' dopo 'stripTrailingZeros()', e quindi 'scale()' è '-1', non' 0'. –

+0

@EricWang Il codice usa 'Math.max (0, ...)' così finirai con '0' come previsto. – Gili

+0

Ok, non l'ho notato. –

0

opzione migliore che ho trovato finora (non avendo bisogno toString + indice):

public static int digitsStripTrailingZero(BigDecimal value) 
{ 
    return digits(value.stripTrailingZeros()); 
} 

public static int digits(BigDecimal value) 
{ 
    return Math.max(0, value.scale()); 
} 
0

senza dover convertire in stringa, dovrebbe essere più efficiente di utilizzare la bilancia direttamente:

private int getNumberOfDecimalPlaces(BigDecimal bigDecimal) 
    { 
    int scale = bigDecimal.stripTrailingZeros().scale(); 
    return scale>0?scale:0; 
    } 
Problemi correlati