BigDecimal, división y MathContext - comportamiento muy extraño
-
22-09-2019 - |
Pregunta
CentOs 5,4, OpenJDK Runtime Environment (build 1.6.0-b09)
MathContext context = new MathContext(2, RoundingMode.FLOOR);
BigDecimal total = new BigDecimal("200.0", context);
BigDecimal goodPrice = total.divide(BigDecimal.valueOf(3), 2, RoundingMode.FLOOR);
System.out.println("divided price=" + goodPrice.toPlainString());
// prints 66.66
BigDecimal goodPrice2 = total.divide(BigDecimal.valueOf(3), new MathContext(2, RoundingMode.FLOOR));
System.out.println("divided price2=" + goodPrice2.toPlainString());
// prints 66
ERROR?
Solución
Javadoc para la primera situación:
Devuelve un BigDecimal cuyo valor es (este / divisor), y cuya escala es el especificado. Si el redondeo se debe realizar para generar un resultado con la escala especificada, se aplica el modo de redondeo especificado.
y el Javadoc para la segunda situación:
Devuelve un BigDecimal cuyo valor es (este / divisor), con redondeo de acuerdo con la configuración de contexto.
refiriéndose a la Javadoc para MathContext obtenemos:
Los objetos inmutables que encapsulan la configuración de contexto que describen ciertas reglas para los operadores numéricos, tales como los llevados a cabo por el clase BigDecimal. La base independiente ajustes son: precisión: el número de dígitos a ser utilizado para una operación; resultados se redondean a esta precisión roundingMode: un objeto RoundingMode que especifica el algoritmo para ser utilizado para el redondeo.
Así que en el primer caso, se especifica una escala de 2, lo que significa que alrededor de 2 decimales de precisión, donde el redondeo se realiza como una función del piso. El segundo cálculo tiene una precisión especificada de 2, que es redondeado a dos dígitos de precisión, en el que el redondeo es una función piso. Así, en el primer caso, que pidió 2 dígitos tras el punto decimal , y en el segundo, sólo se le preguntó por 2 dígitos. Si, por ejemplo, le hubieran preguntado por 4 dígitos en su MathContext, se obtendría 66.66 al responder.
Así que no creo que esto es un error tanto como que los dos métodos no llevan a cabo el mismo cálculo.
Otros consejos
Esto es completamente el comportamiento esperado. Creo que comete el error de redondeo y mezclar (escala) y la precisión.
total.divide(BigDecimal.valueOf(3), 2, RoundingMode.FLOOR)
A continuación se reemplaza el del MathContext y utilizar un redondeo.
total.divide(BigDecimal.valueOf(3), new MathContext(2, RoundingMode.FLOOR))
Aquí se ajusta una precisión de 2 y recibir sólo 2 dígitos.
Al leer el Javadoc de la brecha (BigDecimal, MathContext) método, parece que sólo el modo de redondeo del contexto se tiene en cuenta.