Translate

quarta-feira, 21 de agosto de 2013

Como multiplicar sem perder precisão

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:

      double tempValue = value * 100;
      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) {  

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

}

public static int doubleToInt(double value, int scale) {  

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

}

public static int floatToInt(float value, int scale) {  

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

}

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

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() { 

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 >= 100.0d) {
break;
}

decimalValue += 0.10d;

singleNumber = (long) (decimalValue * 100); 

if (singleNumber != doubleToLong(decimalValue, 2)) {
if (erros <= 5) {
System.err.println(String.format("  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() { 

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 >= 100.0d) {
break;
}

decimalValue += 0.10d;

singleNumber = doubleToLong(decimalValue, 2); 

if (singleNumber != doubleToLong(decimalValue, 2)) {
if (erros <= 100) {
System.err.println(String.format("  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
  ERROR: Original: 0,90999999999999990000, After: 90
  ERROR: Original: 4,81000000000000000000, After: 480
  ERROR: Original: 4,90999999999999900000, After: 490
  ERROR: Original: 5,00999999999999900000, After: 500
  ERROR: Original: 5,10999999999999850000, After: 510
  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.

Nenhum comentário:

Postar um comentário