2012-02-28 65 views
32

Tengo un XSD que me obliga a utilizar un BigDecimal para un lat/lon. Actualmente tengo el lat/lon como dobles y los convierto en BigDecimal, pero solo debo usar unos 12 lugares de precisión. No he podido averiguar cómo configurar eso. Puede alguien ayudarme con esto?Establecer precisión específica de un BigDecimal

+0

¿Ha intentado dar al constructor un MathContext? – bdares

+1

Si veo correctamente, está hablando de lugares decimales, no de precisión de coma flotante. Al menos la respuesta aceptada dará como resultado 12 lugares decimales, no 12 dígitos de precisión. – vbence

Respuesta

60

Puedes usar setScale() p. Ej.

double d = ... 
BigDecimal db = new BigDecimal(d).setScale(12, BigDecimal.ROUND_HALF_UP); 
+1

Duh. Mirando hacia atrás al error que obtuve, dijo que el redondeo era necesario. Solo había hecho .setScale (12). Una vez que confirme que funciona, lo haré como respondí. Gracias. – Jason

+0

No sé por qué no solo usa un método de redondeo predeterminado como el de conversión, solo he visto ROUND_HALF_UP utilizado. Es bastante pedante. ;) –

+1

+1 ya que esto me ayudó con mi automatización de hoy :) – Brian

27

El título de la pregunta se refiere a la precisión. BigDecimal distingue entre escala y precisión. La escala es la cantidad de decimales. Puede pensar en la precisión como el número de significant figures, también conocido como dígitos significativos.

Algunos ejemplos en Clojure.

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

(En Clojure, La M designa un BigDecimal literal. Puede traducir el Clojure a Java si se quiere, pero me parece que es más compacto que el de Java!)

Usted puede fácilmente aumentar la escala:

(.setScale 0.00123M 7) ; 0.0M 

Pero no se puede disminución en la escala exacta de la misma manera:

(.setScale 0.00123M 3) ; ArithmeticException Rounding necessary 

Tendrá que pasar a un modo de redondeo demasiado:

(.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. 

lo tanto, es fácil cambiar la escala. ¿Pero qué hay de la precisión? ¡Esto no es tan fácil como podrías esperar!

Es fácil disminución la precisión:

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

Pero no es obvio cómo aumento la precisión:

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

Para el escéptico, esto es no simplemente una cuestión de ceros finales que no se muestran:

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

Fwiw, Clojure es cuidadoso con ceros a la derecha y se mostrarles:

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

vuelta a la pista ... Usted puede intentar usar un constructor BigDecimal, pero no establece la precisión más alta que cualquier la cantidad de dígitos que especifique:

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

Por lo tanto, no hay una forma rápida de cambiar la precisión. He pasado tiempo luchando con esto mientras escribo esta pregunta y con un proyecto en el que estoy trabajando. Considero esto, en el mejor de los casos, una API CRAZYTOWN y, en el peor de los casos, un error. Gente. ¿Seriamente?

Por lo tanto, mejor que puedo decir, si desea cambiar de precisión, tendrá que hacer estos pasos:

  1. de búsqueda de la precisión actual.
  2. Buscar la escala actual.
  3. Calcular el cambio de escala.
  4. Establecer la nueva escala

Estos pasos, como código de 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 

que sé, se trata de una gran cantidad de pasos que se acaban de cambiar la precisión!

¿Por qué BigDecimal ya no proporciona esto? ¿Pasé por alto algo?

+0

¿Qué pasa con 'BigDecimal.round (new MathContext (precision, RoundingModel.ROUND_HALF_EVEN))'? –

+1

@ PaŭloEbermann ¿Estás preguntando o sugiriendo? ¿Lo has probado? ¿Me importó compartir los resultados detallados de los ejemplos que encontré en mi respuesta? Si es así, creo que merece una respuesta por separado. –

+1

Para cualquiera que sea como yo y no conozca un poco de Clojure, creo que la solución que se muestra aquí es 'x.setScale (x.scale() + p - x.precision(), RoundingMode.HALF_UP)', donde ' x' es el 'BigDecimal' que desea redondear, y' p' es la cantidad de cifras significativas que desea conservar. Me funciona, ¡pero corrígeme si me equivoco! – Nateowami

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

De esta manera puede establecer la precisión específica de BigDecimal.

Aquí el valor de decPrec fue 1,5726903423607562595809913132345426 que se redondea a 1,57267

0

Prueba este código ...

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

Resultado: 0.00000

Cuestiones relacionadas