domingo, 9 de dezembro de 2007

Precisão em Java com tipo float.

Algumas vezes já me deparei com programas e sistemas que apresentavam resultados incorretos nos cálculos com números fracionários. O código abaixo mostra por si só (já que falar não dá né):

public static void main(String[] args) {

float f1 = 45f;
float f2 = 19.8f;
float resFloat = f1 * f2;
System.out.printf("float: %f x %f = %f => %s\n",f1,f2,resFloat,String.valueOf(resFloat));
double d1 = 45;
double d2 = 19.8;
double resDouble = d1 * d2;
System.out.printf("double: %f x %f = %f => %s\n",d1,d2,resDouble,String.valueOf(resDouble));
String s1="45";
String s2="19.8";
BigDecimal bd=new BigDecimal(s1);
bd=bd.multiply(new BigDecimal(s2));
DecimalFormat df=new DecimalFormat();
System.out.printf("BigDecimal %s x %s = %s\n",s1,s2,df.format(bd));

}


Resultado:

float: 45,000000 x 19,799999 = 890,999939 => 890.99994
double: 45,000000 x 19,800000 = 891,000000 => 891.0
BigDecimal 45 x 19.8 = 891


Como podem ver o tipo float perde a precisão com muita facilidade (tem a ver com a forma de armazemanento e a conversão para binário dos números fracionários e outros mistérios!!)
Na internet tem várias explicações. Eis uma delas.

Como normalmente estes sistemas/programas querem apenas uma precisão númerica de 2 a 4 dígitos após a virgula, eu costumo sugerir a classe BigDecimal para solucionar o problema (ela bem que podia ser um wrapper ligada a um tipo bigdecimal deixando as coisas bem mais simples, hehehehe).

Porém, e aqui fica minha tristeza, no J2ME (pelo menos até a CLDC 1.1/MIDP 2.0) não existe a classe BigDecimal. Dai o double é a única solução (no CLDC 1.0, nem o float é solução).

Ou alguém conhece alguma solução melhor em J2ME para o armazenamento de números fracionários?