Como multiplicar sem perder precisão
Autor: Wiliam Silva | Publicado: 2013-08-21T14:15:00-03:00

Conforme o último artigo, na plataforma Intel ocorre problema de precisão para número ponto flutuante quando se tenta multiplicar com o operador padrão "*".

Este problema é bastante severo quando o sistema trata de transações comerciais ou financeiras, porque são dois tipos de transações que não podem ter erro de precisão, senão os números não batem na contabilidade e em casos mais graves, quando se trata de sistemas de recargas, isso é mais crítico ainda porque é a população humana quem sofre o ônus da falha. Claro, existem "n" impactos.

Para contornar o problema em qualquer linguagem, basta usar a seguinte lógica:

&nbsp; &nbsp; &nbsp; double tempValue = value * 100;
&nbsp; &nbsp; &nbsp; tempValue = Math.floor(tempValue + 0.5);

Pronto!

Não, vamos fazer uma classe com as funções e testes dos erros e da solução:

Classe FloatToInteger

O nome da classe não tem nada a ver com os tipos numéricos do Java, é apenas uma forma de dizer que esta classe trata de transformar número flutuantes em números inteiros.

Dentro desta classe existem as seguintes funções auxiliares:


doubleToLong - está função converte double para long;
doubleToInt - está função converte double para int;
floatToInt - está função converte float para int;
floatToShort - está função converte float para short.


Para todas as funções, os parâmetros são value e scale:


value - é o valor flutuante;
scale - é o número de casas decimais e value para a função saber como transformar o flutuante em inteiro.


Código

public class FloatToInteger {

 public static long doubleToLong(double value, int scale) { &nbsp;

  double tempValue = value * Math.pow(10, scale);
  tempValue = Math.floor(tempValue + 0.5);
  return (long) tempValue;

 }

 public static int doubleToInt(double value, int scale) { &nbsp;

  double tempValue = value * Math.pow(10, scale);
  tempValue = Math.floor(tempValue + 0.5);
  return (int) tempValue;

 }

 public static int floatToInt(float value, int scale) { &nbsp;

  double tempValue = value * Math.pow(10, scale);
  tempValue = Math.floor(tempValue + 0.5);
  return (int) tempValue;

 }

 public static short floatToShort(float value, int scale) { &nbsp;

  double tempValue = value * Math.pow(10, scale);
  tempValue = Math.floor(tempValue + 0.5);
  return (short) tempValue;

 }

 public static void main(String[] args) {

  troubleSimulation();
  solutionSimulation();

 }

 public static void troubleSimulation() {&nbsp;

  int erros = 0;

  /***********************
   * Provocar até 5 erros para amostragem do problema
   * ao usar a múltiplicação comum
   ***********************/
  double decimalValue = 0.01d;
  long singleNumber = 0L;

  System.out.println("1) AMOSTRAGEM DO PROBLEMA - Com o problema");

  while (true) {

   if (decimalValue &gt;= 100.0d) {
    break;
   }

   decimalValue += 0.10d;

   singleNumber = (long) (decimalValue * 100);&nbsp;

   if (singleNumber != doubleToLong(decimalValue, 2)) {
    if (erros &lt;= 5) {
     System.err.println(String.format(" &nbsp;ERROR: Original: %.20f, After: %d", decimalValue, singleNumber));
    }
    erros++;
   }

  }

  System.out.println(String.format("População: %.0f, Total de erros: %d\n", 100 / 0.10, erros));

 }

 public static void solutionSimulation() {&nbsp;

  int erros = 0;
  
  /***********************
   * Provocar até 5 erros para amostragem do problema
   * ao usar a múltiplicação comum
   ***********************/
  double decimalValue = 0.01d;
  long singleNumber = 0L;

  System.out.println("2) AMOSTRAGEM DE PROBLEMAS - Após com a solução");

  while (true) {

   if (decimalValue &gt;= 100.0d) {
    break;
   }

   decimalValue += 0.10d;

   singleNumber = doubleToLong(decimalValue, 2);&nbsp;

   if (singleNumber != doubleToLong(decimalValue, 2)) {
    if (erros &lt;= 100) {
     System.err.println(String.format(" &nbsp;ERROR: Original: %.20f, After: %d", decimalValue, singleNumber));
    }
    erros++;
   }

  }

  System.out.println(String.format("População: %.0f, Total de erros: %d\n", 100 / 0.10, erros));

 }

}



Resultado dos testes

Observe o método main, ele invoca duas funções, uma para simular o problema e outro para comprovar a solução.

Resultado




1) AMOSTRAGEM DO PROBLEMA - Com o problema
&nbsp; ERROR: Original: 0,90999999999999990000, After: 90
&nbsp; ERROR: Original: 4,81000000000000000000, After: 480
&nbsp; ERROR: Original: 4,90999999999999900000, After: 490
&nbsp; ERROR: Original: 5,00999999999999900000, After: 500
&nbsp; ERROR: Original: 5,10999999999999850000, After: 510
&nbsp; ERROR: Original: 5,20999999999999800000, After: 520
População: 1000, Total de erros: 385

2) AMOSTRAGEM DE PROBLEMAS - Após com a solução
População: 1000, Total de erros: 0



Boa sorte!



Até mais.