[RESOLVIDO] - Android Studio - Multiplicação não dá o resultado correto

Boa noite, pessoal! Seguinte:

Estou fazendo uma calculadora simples, mas estou me deparando com uma coisa estranha. Multiplico dois inteiros e o resultado é absolutamente incorreto e quando eu multiplico dois doubles o resultado sai, só que no final ele coloca E mais um número, por exemplo:

  • Se eu multiplicar os valores do tipo inteiro: 9999*9999 = 99980001 (CORRETO);
  • Se eu multiplicar os valores do tipo inteiro: 99999*99999 = 1409865409 (ABSOLUTAMENTE INCORRETO! É PRA DAR 9.999.800.001);
  • Se eu multiplicar os valores do tipo inteiro: 999999*999999 = -729379967 (ABSOLUTAMENTE INCORRETO! É PRA DAR 999.998.000.001);
  • Se eu multiplicar os valores do tipo double: 9999*9999 = 99980001E7 (ACRESCENTOU E7 NO FINAL);
  • Se eu multiplicar os valores do tipo double: 99999*99999 = 9999800001E9 (ACRESCENTOU E9 NO FINAL);
  • Se eu multiplicar os valores do tipo double: 999999*999999 = 999998000001E11 (ACRESCENTOU E11 NO FINAL);

Uma coisa que observei foi:

  • 9999*9999 tem 8 dígitos e o 7 do E7 é 8-1=7
  • 99999*99999 tem 10 dígitos e o 9 do E9 é 10-1=9
  • 999999*999999 tem 12 dígitos e o 11 do E11 é 12-1=11
  • 9999999.0*9999999.0 = 9.9999980000001E13 (14-1=13) e assim vai…

O que será isso?

Não é pra o double fazer isso. Sem falar no inteiro neh, que cagada, não sei oq está acontecendo pra ele fazer isso, e oq tô fazendo é apenas:

resultado = valorInicial*valorFinal;

Alguém?

As operações realizadas dependem dos tipos usados. A operação 99999 * 99999 usando variáveis int provavelmente está extrapolando o limite de representação do tipo int. Com o tipo long, esse problema não ocorre (mas ainda há um limite, só que maior). Possivelmente, o mesmo ocorre com o double, além de quê você pode querer formatar a exibição (pra não exibir o ENN no final).

Veja um exemplo em: https://ideone.com/l9vjdn

Se pretende suportar operações com números grandes, provavelmente vai querer usar outros tipos, como BigInteger e BigDecimal, em vez de int e double.

Abraço.

O maior valor positivo que um int comporta, é 2.147.483.647, então, para conseguir representar o 9.999.800.001 ou o 999.998.000.001, você precisa de uma variável do tipo long.

Está certo, o nome disso é notação científica, o "E" significa "vezes 10 elevado à potência".
Por padrão se os valores double forem menores que 10^-3 ou maiores ou iguais à 10^7 o Java apresentará eles utilizando notação científica.
Se você não quiser visualizar em notação científica, terá de formatar o valor.

Não tem nada de “cagada” aí, isso é o funcionamento normal da linguagem, da forma que ela foi definida. Entender como funcionam os tipos é essencial para não cair nessas “armadilhas”.

Conforme descrito aqui, cada tipo do Java tem um tamanho específico. No caso de int, ele usa 32 bits, e o maior valor que ele pode ter é 231-1 (ou seja, 2.147.483.647 - dois bilhões, cento e quarenta e sete milhões, etc). Se você somar 1 a esse valor, ocorre um overflow e ele passa a ser negativo (no caso, ele passa a ser -2147483648, que é o menor valor possível para um int):

int n = 2147483647;
System.out.println(n + 1); // -2147483648

Pense em um odômetro de carro: a maior quilometragem que ele pode indicar é 999999. Se você andar mais um quilômetro, ele volta para o menor valor possível, que é 000000. O mesmo ocorre com int, a diferença é que o menor valor possível é um número negativo. E não adianta tentar aumentar isso, a quantidade de dígitos é fixa (assim como o tamanho de um int é fixo, então um número maior do que a capacidade deste não vai “caber” na quantidade de bits que ele usa).

Se você está trabalhando com números que podem extrapolar os limites de um int, tem que usar um tipo “maior”. No caso, poderia ser um long, que usa 64 bits (portanto, suporta números bem maiores do que um int):

long n = 999999L * 999999L;
System.out.println(n); // 999998000001

Repare que escrevi os números com o prefixo L para “forçar” que sejam long, pois se eu escrever somente 999999, este é interpretado como um int e a conta será feita com int's, e o resultado ficará errado do mesmo jeito. Forçando os números para long, eu obtenho o resultado correto.

Vale lembrar que todos os tipos possuem limites, e no caso do long, o valor máximo é 263-1 (ou seja, 9.223.372.036.854.775.807 - mais de 9 quintilhões). E se alguma conta ultrapassar este limite, também ocorrerá o overflow:

long n = 999999999999999999L * 999999999999999999L;
System.out.println(n); // -7527149226598858751  <-- overflow!

Se precisa de números ainda maiores, aí tem que usar classes como BigInteger. Só que aí fica um pouco mais chato fazer as contas:

BigInteger n = BigInteger.valueOf(999999999999999999L);
n = n.multiply(n);
System.out.println(n); // 999999999999999998000000000000000001

Já no caso do double, a questão é outra. Você está confundindo o número com a representação do número.

Por exemplo, o número 2. Ele é apenas um “conceito”, um valor… numérico, que representa a ideia de uma determinada quantidade (“duas coisas”).

Mas esse mesmo valor pode ser representado de diferentes formas: como o dígito 2, como 2.0 (ou 2,00000), ou como o texto “dois”, “two”, etc. Todas essas formas são diferentes (contém caracteres diferentes), mas todas elas representam o mesmo valor numérico (o mesmo número).

Sendo assim, se você imprimir um double, ele usa o formato especificado por Double.toString, cuja documentação diz:

se o número é menor que 10-3 ou maior que 107, ele é representado em notação científica

Que é justamente o que aconteceu. O resultado de 9999 * 9999 é 99980001, que é maior que 107 e por isso é mostrado como 9.9980001E7 (o “E7” indica que esse número corresponde a 9.9980001 * 107). Se quer mostrar o número de forma diferente, basta formatá-lo:

double n = 9999 * 9999;
System.out.println(n); // 9.9980001E7
System.out.printf("%.0f", n); // 99980001

Bom,

Eu não quero só mostrar, quero trabalhar com esse valor para outras operações, mas entendi oq vcs falaram e vou implementar. Obg!!

Então basta escolher o tipo mais adequado, que suporte os valores que você precisa.

Vale lembrar que double e float possuem imprecisão (entenda melhor lendo aqui), então se os valores não possuem casas decimais, um long ou BigInteger são mais adequados.

Exatamente isso

Pessoal, ainda uma dúvida, como o assunto é o mesmo, estou aproveitando o tópico. hugokotsubo falou:

Preciso pegar os valores, não só a exibição.

Na calculadora, quando 99.999 * 99.999 o resultado é 9.999.800.001, mas quando vou pro meu app e faço a multiplicação normalmente, usando o Double , ele retorna o valor 9,999800001E9.
Eu entendi oq @hugokotsubo falou, então por que nas calculadoras o resultado é 9.999.800.001 (Não tem o E)? Por que o meu app não retorna 9.999.800.001 como nas calculadoras? Tipo, não quero abolir o E, só quero que ele resulte com o E quando for igual aos resultados das calculadoras. Nesse caso, oq faço pra ele retornar valores normalmente como 99.999 * 99.999 = 9.999.800.001 e só exibir o E quando for o caso, por exemplo como na calculadora, 999.999.999 * 999.999.999 = 9,99999998e+17?

Já fiz os cálculos com int, long, BigInteger, BigDecimal, mas o double chega mais próximo e de forma simples de se implementar os cálculos.

No aguardo, galera.

Como já foi dito, quando um double é maior que 107, isto é, 10.000.000, por padrão o Java o apresenta em notação científica.
9,999800001E9 é o mesmo que 9,999800001 x 109 que é o mesmo que 9.999.800.001, que é maior que 10.000.000.

Porque o seu app é em Java que, por padrão, usa notação científica para valores maiores que 107 e as calculadoras que você usou usam notação científica a partir de outra magnitude de valores.

Repetindo:
Se você não quer visualizar em notação científica, vai ter que formatar o resultado.

2 curtidas

Perfeito, @staroski, caiu a ficha! Vish uma besteira dessas, vc tá certo, já resolvi a bronca! Obg!