1. Arredondamento de números reais
Como já discutimos, quando um número real é atribuído a uma int
variável, ele sempre é arredondado para o inteiro menor mais próximo — a parte fracionária é simplesmente descartada.
Mas é fácil imaginar uma situação em que um número fracionário precisa ser arredondado para o inteiro mais próximo em qualquer direção ou até mesmo arredondado para cima. O que você faz neste caso?
Para isso e para muitas situações semelhantes, Java possui a Math
classe, que possui os métodos round()
, ceil()
e floor()
.
Math.round()
método
O Math.round()
método arredonda um número para o inteiro mais próximo:
long x = Math.round(real_number)
Mas há outra nuance aqui: esse método retorna um long
inteiro (não um int
). Como os números reais podem ser muito grandes, os criadores do Java decidiram usar o maior tipo inteiro disponível do Java: long
.
Assim, se um programador deseja atribuir o resultado a uma int
variável, ele deve indicar explicitamente ao compilador que aceita a possível perda de dados (caso o número resultante não se encaixe em um int
tipo).
int x = (int) Math.round(real_number)
Exemplos:
Declaração | Resultado |
---|---|
|
|
|
|
|
|
Math.ceil()
método
O Math.ceil()
método arredonda um número para um inteiro. Aqui estão exemplos:
Declaração | Resultado |
---|---|
|
|
|
|
|
|
Math.floor()
método
O Math.floor()
método arredonda um número para baixo para um inteiro. Aqui estão exemplos:
Declaração | Resultado |
---|---|
|
|
|
|
|
|
Obviamente, ao arredondar um número para baixo para um inteiro, é mais fácil simplesmente usar um operador de conversão de tipo:(int)
Declaração | Resultado |
---|---|
|
|
Se você achar difícil lembrar desses nomes, uma breve aula de inglês ajudará:
Math
significa matemáticaRound
significa redondoCeiling
significa tetoFloor
significa chão
2. Como os números de ponto flutuante são estruturados
O double
tipo pode armazenar valores no intervalo de a . Essa enorme gama de valores (em comparação com o tipo) é explicada pelo fato de que o tipo (assim como ) tem uma estrutura interna completamente diferente dos tipos inteiros. Internamente, o tipo codifica seu valor como dois números: o primeiro é chamado de mantissa e o segundo é chamado de expoente .-1.7*10308
+1.7*10308
int
double
float
double
Digamos que temos o número 123456789
e o armazenamos em uma double
variável. Quando o fazemos, o número é convertido em , e internamente o tipo armazena dois números — e . O significando ("parte significativa do número" ou mantissa) é destacado em vermelho, enquanto o expoente é destacado em azul.1.23456789*108
double
23456789
8
Essa abordagem permite armazenar números muito grandes e muito pequenos. Mas como a representação do número é limitada a 8 bytes (64 bits) e alguns dos bits são usados para armazenar o expoente (assim como o sinal da mantissa e o sinal do expoente), os dígitos máximos disponíveis para representar a mantissa é 15 .
Esta é uma descrição muito simplificada de como os números reais são estruturados.
3. Perda de precisão ao trabalhar com números reais
Ao trabalhar com números reais, tenha sempre em mente que os números reais não são exatos . Sempre pode haver erros de arredondamento e erros de conversão ao converter de decimal para binário. Além disso, a fonte mais comum de erro é a perda de precisão ao adicionar/subtrair números em escalas radicalmente diferentes.
Este último fato é um pouco alucinante para programadores novatos.
Se subtrairmos de , obtemos .1/109
109
109
Subtraindo números em escalas radicalmente diferentes | Explicação |
---|---|
|
O segundo número é extremamente pequeno , o que fará com que seu significante (destacado em cinza) seja ignorado. Os 15 dígitos significativos são destacados em laranja. |
O que podemos dizer, programação não é o mesmo que matemática.
4. Armadilha ao comparar números reais
Outro perigo está à espreita dos programadores quando eles comparam números reais. Surge ao trabalhar com números reais, porque os erros de arredondamento podem se acumular. O resultado é que há situações em que se espera que os números reais sejam iguais, mas não são. Ou vice-versa: espera-se que os números sejam diferentes, mas são iguais.
Exemplo:
Declaração | Explicação |
---|---|
|
O valor da variável a será 1000000000.0 O valor da variável c será 1000000000.0 (o número na b variável é excessivamente pequeno) |
No exemplo acima, a
e c
não deveriam ser iguais, mas são.
Ou vamos pegar outro exemplo:
Declaração | Explicação |
---|---|
|
O valor da variável a será 1.0 O valor da variável b será1.0 |
5. Um fato interessante sobrestrictfp
Java tem uma strictfp
palavra-chave especial ( ponto flutuante estrito ) , que não é encontrado em outras linguagens de programação . E sabe porque você precisa disso? Isso piora a precisão das operações com números de ponto flutuante. Aqui está a história de como surgiu:
GO TO FULL VERSION