Este es un problema divertido.
La idea detrás de la respuesta de Timons es que especifique un épsilon que representa la precisión más pequeña que un doble legal puede ser. Si sabe en su aplicación que nunca necesitará precisión por debajo de 0.00000001 entonces, lo que sugiere es suficiente para obtener un resultado más preciso muy cerca de la verdad. Útil en aplicaciones donde conocen por adelantado su máxima precisión (por ejemplo en finanzas para precisiones de moneda, etc.)
Sin embargo, el problema fundamental al tratar de redondearlo es que cuando se divide por un factor para reescalarlo, en realidad se introduce otra posibilidad para problemas de precisión. Cualquier manipulación de dobles puede introducir problemas de imprecisión con frecuencia variable. Especialmente si usted está tratando de ronda en un dígito significativo (por lo que sus operandos son < 0), por ejemplo, si se ejecuta el siguiente código con Timons:
System.out.println(round((1515476.0) * 0.00001)/0.00001);
resultará en 1499999.9999999998
donde el objetivo aquí es redondear en las unidades de 500000 (es decir, queremos 1500000)
De hecho, la única manera de estar completamente seguro de haber eliminado la imprecisión es pasar por un BigDecimal para escalar. p.ej.
System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue());
Usando una mezcla de la estrategia épsilon y la estrategia de BigDecimal le dará un control preciso sobre su precisión. La idea es que el épsilon te acerca mucho y luego el BigDecimal eliminará cualquier imprecisión causada al volver a escalar después. Aunque usar BigDecimal reducirá el rendimiento esperado de su aplicación.
Se me ha indicado que el paso final de usar BigDecimal para reescalarlo no siempre es necesario en algunos casos de uso cuando puede determinar que no hay valor de entrada que la división final puede reintroducir un error. Actualmente no sé cómo determinarlo adecuadamente, así que si alguien sabe cómo, me gustaría saberlo.
No hay manera de que pueda "evitar" por completo los errores aritméticos de coma flotante. La cantidad de bits utilizados para representar un número siempre será finita. Todo lo que puede hacer es usar tipos de datos con mayor precisión (bits). –
Eso es verdad. Editaré mi respuesta para reflejar con mayor precisión el uso de BigDecimal. –
Agregaré una nota que BigDecimal _division_ necesita tratarse de forma un poco diferente que +, -, * ya que de forma predeterminada lanzará una excepción si no puede devolver un valor exacto (1/3, por ejemplo). En una situación similar utilicé: BigDecimal.valueOf (a) .divide (BigDecimal.valueOf (b), 25, RoundingMode.HALF_UP) .doubleValue(), donde 25 si los dígitos máximos de precisión (más que los necesarios para el resultado doble)) –