Dúvidas sobre os tipos float e double

Oi pessoal,

estou começando a estudar Java agora e resolvi entrar aqui pra tirar umas dúvidas sobre os tipos float e double.

Então, eu estava lendo aqui sobre esses tipos e vi os intervalos de valores positivos e negativos que eles podem armazenar. No caso do float o intervalo positivo é:

float (32 bits) -> Intervalo positivo: 1.40129846432481707e-45 a 3.4028234663852886E+38

(…)

Minha dúvida é a seguinte: como esses intervalos foram calculados para esses dois tipos? Como um número ‘quebrado’ é representado na forma binária e como o computador entende que o número é quebrado?
E o que significa cada intervalo desses, visto que variáveis desses tipos não têm essa precisão tão grande (li isso em uma apostila. Por exemplo: uma variável float tem precisão de umas 7 casas decimais. É isso mesmo?).

Espero ter sido clara e desculpem se perguntei besteira! :wink:

Obrigada desde já,

Lívia.

Ok, eu recorri ao google e consegui encontrar algumas respostas para as minhas perguntas. Agora eu sei como um número quebrado é representado na forma binária e como o computador entende que o número é quebrado.

Mas continuo sem saber como esses intervalos de valores são calculados para float e double, e também como fica a história da precisão de variáveis desse tipo.

Lívia, não sei ao certo responder sua pergunta, mas parece que esse cálculo tem a ver com as potências de 2, pois o que o computador entende são os núemros binários, como você mesmo escreveu. Tente arrumar uma forma de ver o resultado de 2 elevado a 32 e compare com essa cadeia de valores que você postou. Acho que é assim.

Com relação à precisão de uma variável float, fico te devendo uma resposta, pois não sei também.

Bom, já esclareci (algumas das) minhas perguntas, mas mesmo assim vou colocar as respostas aqui, caso alguém venha a ter as mesmas dúvidas. :slight_smile: Na verdade, não posso garantir que minhas respostas estão certas, mas foi isso o que eu entendi do que eu li. No fim do post vou colocar todas as minhas referências bibliográficas.

Então, é o seguinte: existe um padrão para a representação e aritmética de pontos flutuantes em um computador. Esse padrão é o IEEE 754, e deve ser seguido por fabricantes de computadores, compiladores, linguagens, etc.

De acordo com o padrão IEEE 754, cada número deve ser representado da seguinte forma: o bit mais significativo indica o sinal, os bits menos significativos representam a mantissa (parte fracionária) e os bits intermediários representam o expoente.

No caso de um número de 32 bits (0 a 31), ou uma variável do tipo float, fica assim:

bit 31: [color=red]SINAL [/color]
bits 23 - 30: [color=red]EXPOENTE [/color]
bits 00 - 22: [color=red]MANTISSA [/color]

Sendo que o expoente é deslocado de 127 unidades. Por exemplo, o número 0000 0000 representa o expoente -127, e o número 1111 1111 representa o expoente +128.

Mas, se a mantissa representa só a parte fracionária, como eu represento a parte inteira do número (que obviamente, só poderá ser 0 ou 1)? Bom, para isso existe a normalização: os números normalizados têm a parte inteira igual ‘1’. Ou seja, não existe um bit que guarde essa informação, ela simplesmente está implícita no número. Mas também existem os números desnormalizados, que têm parte inteira igual a ‘0’.

E como saber se um número é normalizado ou desnormalizado? Um número desnormalizado sempre possui os bits 23 a 30 (expoente) igual a 0000 0000. Mas estes bits não mais têm a função de expoente, e sim estão simplesmente deixando claro que a parte inteira deste número é 0.
Mas então onde é representado o expoente de um número desnormalizado? Ele não é representado: para números desnormalizados, o expoente é fixo e igual a -126.
Isso também signfica que o menor expoente de um número normalizado é -126, já que ele não pode ter os bits de expoente igual a 0000 0000 (-127).

E como representar valores como 0, infinito ou valores inválidos? Da seguinte forma:

[b]±Zero: [color=red]X 0000 0000 00000000000000000000000[/color]

±Inf. : [color=red]X 1111 1111 00000000000000000000000[/color]

NaN: [color=red]X 1111 1111 XXXXXXXXXXXXXXXXXXXXXXX[/color] [/b], exceto para a mantissa = 0.

OBS: NaN = (Not A Number).

Ou seja, o expoente máximo (+128) é exclusivo para representar infinito e NaN. Por isso, para representar números finitos, o meu expoente máximo deve ser +127.
Com essas informações, já podemos calcular os intervalos de valores de pontos flutuantes que 32 bits podem armazenar:

Na forma normalizada, o maior número positivo que pode ser guardado é:

Maior positivo normalizado:
=0 1111 1110 11111111111111111111111
= + 1,11111111111111111111111 x 2^(127)
= [1+(1-2^(-23))].2^(127)
= [2-2^(-23))].2^(127)
= [color=red]3,4028234663852885981170418348452e+38
[/color]

E o menor positivo é:

Menor positivo normalizado:
= 0 0000 0001 0000000000000000000000
= + 1,0000000000000000000000 x 2^(-126)
= 2^(-126)
= [color=red]1,1754943508222875079687365372222e-38
[/color]

Menor positivo desnormalizado:
= 0 0000 0000 0000000000000000000001
= 0,0000000000000000000001 x 2^(-126)
= 2^(-23).2^(-126)
= [color=red]1,4012984643248170709237295832899e-45
[/color]

Os intervalos dos valores negativos são calculados de maneira análoga.

E é assim que os intervalos são calculados! :slight_smile:

Tudo bem, agora já sabemos como os números decimais quebrados são armazenados em 32 bits da memória do computador. Como calcular agora a precisão? Ou seja, quando eu digito um número decimal quebrado (X) no computador e peço para ele armazenar este número em 32 bits, o número armazenado (X’) não será, na grande maioria das vezes, exatamente o número digitado X, e sim uma aproximação dele (isso você vai entender se souber converter decimais quebrados para binários quebrados). A precisão do tipo float, então, é a diferença entre X e X’.

Ah, atenção para uma coisa: não devemos confundir a precisão dos números armazenados com a exatidão dos mesmos! Assim, por mais que os 32 bits possam representar números com até dezenas de casas decimais, isto não significa que sua precisão será de dezenas de casas decimais.

Tomemos o número 15,87 como exemplo. Ele seria representado em 32 bits da seguinte maneira:

15,87 (dec) = 1111,11011110101110000101 … (bin)

1111,11011110101110000101 = 1,11111011110101110000101 x 2³

Sinal: 0
Expoente: 1000 0010 (3+127)
Mantissa: 11111011110101110000101

Mas essa representação não é exata, pois 15,87 não é exatamente 1111,11011110101110000101 em binário. Isso é uma aproximação, a melhor aproximação que pode ser feita com os 32 bits.
Agora vamos fazer a transformação de volta para saber que número decimal é este realmente.

1111,11011110101110000101 (bin) = 15,869999885 (dec)

Neste caso, a precisão foi de 15,87 - 15,869999885 = 0,000000115. Ou seja, 6 casas decimais.

Então, como estudar a precisão para qualquer número? Como eu saberei, para um determinado número X, quantas casas decimais de X’ serão ‘verdadeiras’?

Para o caso do tipo float, ou 32 bits, essa precisão deve estar entre 6 e 9 casas decimais (exceto nos casos em que a conversão é exata, daí a precisão vai ser bem maior).

Tem um monte de coisa ainda pra se entender, mas ainda não tive tempo de estudar tudo!

Pra quem se interessar, os materiais que explicam essas coisas são esses:

http://docs.sun.com/source/806-3568/
http://babbage.cs.qc.edu/courses/cs341/IEEE-754references.html

É isso, e desculpem quaisquer erros!

Lívia.