web-dev-qa-db-fra.com

Définir la précision spécifique d'un BigDecimal

J'ai un XSD qui me demande d’utiliser un BigDecimal pour un lat/lon. J'ai actuellement le lat/lon sous forme de doublons et je les convertis en BigDecimal, mais je ne dois utiliser que 12 emplacements de précision. Je n'ai pas été capable de comprendre comment régler cela. Est-ce que quelqu'un peut m'aider avec ça?

41
Jason

Vous pouvez utiliser setScale () p. Ex.

double d = ...
BigDecimal db = new BigDecimal(d).setScale(12, BigDecimal.ROUND_HALF_UP);
82
Peter Lawrey

Le titre de la question demande de la précision. BigDecimal fait la distinction entre échelle et précision. L'échelle est le nombre de décimales. Vous pouvez considérer la précision comme le nombre de chiffres significatifs , également appelé chiffres significatifs.

Quelques exemples à Clojure.

(.scale     0.00123M) ; 5
(.precision 0.00123M) ; 3

(Dans Clojure, la M désigne un littéral BigDecimal. Vous pouvez traduire le Clojure en Java si vous le souhaitez, mais je le trouve plus compact que Java!)

Vous pouvez facilement augmenter l'échelle:

(.setScale 0.00123M 7) ; 0.0012300M

Mais vous ne pouvez pas diminuer l’échelle de la même manière:

(.setScale 0.00123M 3) ; ArithmeticException Rounding necessary

Vous devrez également passer un mode d'arrondi:

(.setScale 0.00123M 3 BigDecimal/ROUND_HALF_EVEN) ;
; Note: BigDecimal would prefer that you use the MathContext rounding
; constants, but I don't have them at my fingertips right now.

Donc, il est facile de changer d'échelle. Mais qu'en est-il de la précision? Ce n'est pas aussi facile que vous pourriez l'espérer!

Il est facile de diminuer la précision:

(.round 3.14159M (Java.math.MathContext. 3)) ; 3.14M

Mais il n’est pas évident de savoir comment augmenter la précision:

(.round 3.14159M (Java.math.MathContext. 7)) ; 3.14159M (unexpected)

Pour les sceptiques, c'est pas juste une question de fin de zéros non affichés:

(.precision (.round 3.14159M (Java.math.MathContext. 7))) ; 6 
; (same as above, still unexpected)

FWIW, Clojure fait attention à la fin des zéros et va leur montrer:

4.0000M ; 4.0000M
(.precision 4.0000M) ; 5

De retour sur la bonne voie ... Vous pouvez utiliser un constructeur BigDecimal , mais la précision n'est pas supérieure au nombre de chiffres que vous spécifiez:

(BigDecimal. "3" (Java.math.MathContext. 5)) ; 3M
(BigDecimal. "3.1" (Java.math.MathContext. 5)) ; 3.1M

Donc, il n'y a pas de moyen rapide de changer la précision. J'ai passé du temps à lutter contre cela pendant que je rédigeais cette question et un projet sur lequel je travaillais. Je considère au mieux cela, une API CRAZYTOWN, et au pire un bug. Personnes. Sérieusement?

Donc, le mieux que je puisse dire, si vous voulez changer de précision, vous devrez suivre ces étapes:

  1. Recherchez la précision actuelle.
  2. Recherchez l'échelle actuelle.
  3. Calculez le changement d'échelle.
  4. Définir la nouvelle échelle

Ces étapes, en tant que code Clojure:

(def x 0.000691M) ; the input number
(def p' 1) ; desired precision
(def s' (+ (.scale x) p' (- (.precision x)))) ; desired new scale
(.setScale x s' BigDecimal/ROUND_HALF_EVEN)
; 0.0007M

Je sais, il y a beaucoup d'étapes juste pour changer la précision!

Pourquoi BigDecimal ne le fournit-il pas déjà? Ai-je oublié quelque chose?

32
David J.
 BigDecimal decPrec = (BigDecimal)yo.get("Avg");
 decPrec = decPrec.setScale(5, RoundingMode.CEILING);
 String value= String.valueOf(decPrec);

De cette façon, vous pouvez définir la précision spécifique d'une BigDecimal.

La valeur de decPrec était 1.5726903423607562595809913132345426 Elle est arrondie à 1.57267.

3
E Hashmi

Essayez ce code ...

    Integer perc = 5;
    BigDecimal spread = BigDecimal.ZERO; 
    BigDecimal perc = spread.setScale(perc,BigDecimal.ROUND_HALF_UP);
    System.out.println(perc);

Résultat: 0.00000

0
Hakan Anlamaz