He leído algunas preguntas acerca de flaoting números de punto y sus matemáticas. Me parece que los problemas sólo ocurren a escalas mayores (es decir, 10 + decimales). Ahora mi problema ocurre a los 2 decimales y ya es bastante grande apagado en algunos casos:
Siguiente código:
System.out.println(
String.format("%.2f", BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2).doubleValue()));
System.out.println(
String.format("%.2f", BigDecimal.valueOf(2.41).divide(BigDecimal.valueOf(2.73), 2).doubleValue()));
genera esta salida respectivamente:
0.80
0.89
Si realizo los cálculos a mano (utilizando una fórmula de Google), consigo estos resultados:
0.72351421188
0.88278388278
durante los primeros cálculos de los resultados son muy grandes (~ 0,08 apagado) mientras que para el segundo su muy baja (~ 0,01 apagado).
¿Hay alguna explicación sensata de por qué Thie primer resultado es tan grande? O cualquier forma de obtener el resultado correcto usando BigDecimal
?
Tenga en cuenta que
System.out.println(2.8/3.87);
En realidad devuelve el resultado correcto (,7235142118863048).
Tenga en cuenta también que el String.format
material sólo se utiliza para comprobar si el resultado fue cambiado por lo que no es el caso. BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2).doubleValue()
produce exactamente el mismo resultado.
El problema es que usted está utilizando el mal BigDecimal#divide
para su redondeo a 2 decimales. Estos son los argumentos disponibles para los BigDecimal#divide
métodos:
divide(BigDecimal divisor)
divide(BigDecimal divisor, int roundingMode)
divide(BigDecimal divisor, MathContext mc)
divide(BigDecimal divisor, RoundingMode roundingMode)
divide(BigDecimal divisor, int scale, int roundingMode)
divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
Puesto que usted está usando una divide
con una BigDecimal
y int
argumento, por lo tanto, utiliza el divide(BigDecimal divisor, int roundingMode)
, donde su 2
es el modo de redondeo y NO la escala. En este caso, 2
es en realidad ROUND_CEILING
, y la escala no se especifica.
En su lugar, usted tiene que utilizar el divide(BigDecimal divisor, int scale, int roundingMode)
o divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
. Así que cambiar sus llamadas a:
System.out.println(BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2,
BigDecimal.ROUND_HALF_UP));
System.out.println(BigDecimal.valueOf(2.41).divide(BigDecimal.valueOf(2.73), 2,
BigDecimal.ROUND_HALF_UP));
(Siéntase libre de utilizar un modo de redondeo distinta ROUND_HALF_UP
.)
No estoy seguro de qué escala se utiliza de forma predeterminada, pero el ROUND_CEILING
que ha especificado con las 2
causadas a los problemas en sus cálculos.
En cuanto a los comentarios mencionados, hay tres formas posibles para crear la BigDecimal
con los valores especificados:
BigDecimal.valueOf(2.8)
new BigDecimal(2.8)
new BigDecimal("2.8")
El new BigDecimal(2.8)
todavía da errores de punto flotante, por lo que aconsejaría a utilizar ya sea BigDecimal.valueOf(2.8)
como ya lo hizo, o el constructor de String new BigDecimal("2.8")
.
Todo esto es un poco irrelevante para sus problemas de redondeo sin embargo, ya que el uso correcto del divide
método dará los resultados correctos, independientemente de la BigDecimal
inicialización que ha utilizado: