Flutuantes  XML
Índice dos Fóruns » Assuntos gerais (Off-topic)
Autor Mensagem
renatosilva
GUJ Master

Membro desde: 16/12/2004 17:09:19
Mensagens: 1787
Offline

Antes de mais nada, leiam devagar! leiam devagar

Pessoal, considerem a seguinte conta em Java (tirei de uma discussão interessante no RioJUG)

1.6 * 3 = 4.8


Na base 2 pelo o que calculei, 1.6 = 1.100110011001... e 4.8 = 100.110011001100...

Ou seja, eles são dízimas periódicas. Eu gostaria de saber ou lembrar:

(1) como é feito o processo de arredondamento, já que por mais extensa que seja a mantissa usada (quanto mais períodos pegarmos), mais exato será a conversão de volta pra decimal, porém nunca chegando ao valor exato original. Por exemplo, considerando 4.8, usando um período da dízima mais um bit, em binário, temos:

100.11001 = 4 + 1/2 + 1/4 + 1/32 = 4.78125


(2) É impressão minha ou o número de casas fracionárias em binário é o mesmo o de casas no decimal? Existe alguma relação lógica ou no exemplo acima foi só coincidência?

(3) Eu concluí que é feito um arredondamento até o número de casas que se está trabalhando, no caso de 1.6 * 3 é uma casa, então vamos arredondando:

4.78125 > 4.7813 > 4.781 > 4.78 > 4.8 (valor correto em decimal)

É assim mesmo? Na verdade apenas um período da dízima seria necessário para o arredondamento, assim teríamos 100.1100 = 4.75, que arredonda pra cima e dá 4.8. É isso?

(4) Por que em Java, usando double ou foat (pior), 1.6 * 3 > 4.8? O ponto é: como um arredondamento pode dar mais que 4.8 e não menos? Se pegarmos 100.110011001100... e arredondarmos, como isso pode dar mais que 4.8?

Eu mandei imprimir (1.6f * 3) e (1.6 * 3), que deram isso aqui:
4.800000190734863
4.800000000000001

(5) Sabemos que quando comparamos floats, devemos especificar uma "faixa de tolerância" pela qual eles serão considerados iguais ou não. Mas no caso de cálculos como o do exemplo, não seria melhor truncar o resultado de acordo com o número de casas que se está trabalhando? Ou mesmo arredondar...No exemplo, só consideraríamos a primeira casa fracionária de 4.800000190734863 ou 4.800000000000001, o que dá ambos 4.8. Truncar ou arredondar? Se o 1.6 vêm do usuário, acho que o arredondamento seria melhor...É isso?

Valeu...

EDIT: acho que postei no fórum errado, desculpem, se alguém puder mover eu agradeço...
Ironlynx
Moderador
[Avatar]

Membro desde: 02/05/2003 01:06:41
Mensagens: 3515
Localização: The other side of the screen
Offline

Ótimo tópico Renato!
Mas não é sempre de baixo para cima, por exemplo 4.7813 > 4.781 é para baixo.

Mas isso não tem haver mais com covenção?Por exemplo:
Nos 100 metros rasos(atletismo) vc tem os tempos dos milésimos .500 em diante para cima. Por exemplo: os 9s76 feitos pelo Justin Gatlin foram corrigidos pq ele tinha na verdade 9.766 logo, seu tempo foi revisto para 9s77.

Não basta persistir...tem que prevalecer!
Ironlynx
Anarquista de Sistemas
http://osereojava.blogspot.com/
[WWW]
thingol
Moderador

Membro desde: 29/07/2004 16:10:13
Mensagens: 17543
Offline

renato3110 wrote:Antes de mais nada, leiam devagar! leiam devagar

Pessoal, considerem a seguinte conta em Java (tirei de uma discussão interessante no RioJUG)

1.6 * 3 = 4.8


Na base 2 pelo o que calculei, 1.6 = 1.100110011001... e 4.8 = 100.110011001100...

Ou seja, eles são dízimas periódicas. Eu gostaria de saber ou lembrar:

(1) como é feito o processo de arredondamento, já que por mais extensa que seja a mantissa usada (quanto mais períodos pegarmos), mais exato será a conversão de volta pra decimal, porém nunca chegando ao valor exato original. Por exemplo, considerando 4.8, usando um período da dízima mais um bit, em binário, temos:

100.11001 = 4 + 1/2 + 1/4 + 1/32 = 4.78125


(2) É impressão minha ou o número de casas fracionárias em binário é o mesmo o de casas no decimal? Existe alguma relação lógica ou no exemplo acima foi só coincidência?


Não tinha pensado nisso. Mas é isso mesmo.

O que você chama de "número de casas fracionárias em binário" é a posição do último dígito 1 na representação
fracionária em binário. Só para fazer um aquecimento:

0.1(binário) -> 0,5 (decimal)
0.00001(binário) -> 0,03125 (decimal) = (5 elevado a 5) dividido por (10 elevado a 5)
0.0001 (binário) -> 0,0625 (decimal)
0.1111(binário) -> 0,9375
0.11111(binário) -> 0,96875

Note que esse tal dígito "1" contribui para um dígito "5" na última posição não-zero da representação decimal.
Não estou pondo a prova aqui, mas dá para você ter uma idéia.



(3) Eu concluí que é feito um arredondamento até o número de casas que se está trabalhando, no caso de 1.6 * 3 é uma casa, então vamos arredondando:

4.78125 > 4.7813 > 4.781 > 4.78 > 4.8 (valor correto em decimal)

É assim mesmo?


Não, não tem nada a ver. O arredondamento não é feito "de baixo para cima" como você mostrou.
Ele é feito com a maior precisão possível que permite a você recompor o número (e é por isso que às vezes aparecem certas coisas em Java, como um número 4.79999999999998 ou 4.80000000001, que não aparecem em C++, por exemplo, que costuma imprimir com 6 dígitos após a vírgula.)


Na verdade apenas um período da dízima seria necessário para o arredondamento, assim teríamos 100.1100 = 4.75, que arredonda pra cima e dá 4.8. É isso?




(4) Por que em Java, usando double ou foat (pior), 1.6 * 3 > 4.8? O ponto é: como um arredondamento pode dar mais que 4.8 e não menos? Se pegarmos 100.110011001100... e arredondarmos, como isso pode dar mais que 4.8?

Eu mandei imprimir (1.6f * 3) e (1.6 * 3), que deram isso aqui:
4.800000190734863
4.800000000000001

O arredondamento não é um truncamento, e tanto pode dar mais quanto menos (por propriedades estatísticas). Se ele desse sempre menos, você teria sérios problemas.


(5) Sabemos que quando comparamos floats, devemos especificar uma "faixa de tolerância" pela qual eles serão considerados iguais ou não. Mas no caso de cálculos como o do exemplo, não seria melhor truncar o resultado de acordo com o número de casas que se está trabalhando? Ou mesmo arredondar...No exemplo, só consideraríamos a primeira casa fracionária de 4.800000190734863 ou 4.800000000000001, o que dá ambos 4.8. Truncar ou arredondar? Se o 1.6 vêm do usuário, acho que o arredondamento seria melhor...É isso?


"Truncar resultados intermediários" de acordo com o número de casas que se está trabalhando é convite para ter resultados que dependem da ordem de avaliação da expressão. Isso parece conversa de professor de física de 30 anos atrás. Use a máxima precisão possível nos cálculos intermediários, e arredonde (nunca trunque) somente para visualização.

Leia esta página com atenção:
What Every Computer Scientist Should Know About Floating-Point Arithmetic


Valeu...

EDIT: acho que postei no fórum errado, desculpem, se alguém puder mover eu agradeço...
[WWW]
renatosilva
GUJ Master

Membro desde: 16/12/2004 17:09:19
Mensagens: 1787
Offline

thingol wrote:Não, não tem nada a ver. O arredondamento não é feito "de baixo para cima" como você mostrou.


Mas não é sempre de baixo para cima, por exemplo 4.7813 > 4.781 é para baixo. Mas o arredondamento é feito ou não?

thingol wrote:

(4) Por que em Java, usando double ou foat (pior), 1.6 * 3 > 4.8? O ponto é: como um arredondamento pode dar mais que 4.8 e não menos? Se pegarmos 100.110011001100... e arredondarmos, como isso pode dar mais que 4.8?

Eu mandei imprimir (1.6f * 3) e (1.6 * 3), que deram isso aqui:
4.800000190734863
4.800000000000001


O arredondamento não é um truncamento, e tanto pode dar mais quanto menos (por propriedades estatísticas). Se ele desse sempre menos, você teria sérios problemas.


O que eu tô dizendo é o caso específico do 4.8: como se pega 100.110011001100... e se consegue um valor maior que 4.8?

thingol wrote:"Truncar resultados intermediários" de acordo com o número de casas que se está trabalhando é convite para ter resultados que dependem da ordem de avaliação da expressão. Isso parece conversa de professor de física de 30 anos atrás. Use a máxima precisão possível nos cálculos intermediários, e arredonde (nunca trunque) somente para visualização.

Leia esta página com atenção:
What Every Computer Scientist Should Know About Floating-Point Arithmetic



É que eu reparei que se eu só considerasse a primeira casa, eu teria o valor correto matematicamente (pois never 1,6 x 3 > 4,8 ), e pensei em generalizar isso...

Parece delicioso esse link que você passou, valeuzão!
renatosilva
GUJ Master

Membro desde: 16/12/2004 17:09:19
Mensagens: 1787
Offline

Ironlynx wrote:Ótimo tópico Renato!
Mas não é sempre de baixo para cima, por exemplo 4.7813 > 4.781 é para baixo.

Mas isso não tem haver mais com covenção?Por exemplo:
Nos 100 metros rasos(atletismo) vc tem os tempos dos milésimos .500 em diante para cima. Por exemplo: os 9s76 feitos pelo Justin Gatlin foram corrigidos pq ele tinha na verdade 9.766 logo, seu tempo foi revisto para 9s77.


Não entendi muito bem, mas se está falando do arredondamento do 5 para cima ou para baixo, no caso dos flutuantes eu penso que ele seria para cima, pois à direita você tem uma "sujeira" "puxando" o 5 pra cima... se não é isso eu não entendi...

Eu pensei no arredondamento porque o número é uma dízima e a mantissa não vai dar o número exato, e pensei que arredondando seria uma forma de dar o valor exato...

PS: o GUJ tá com um bug estranho, parece que as mensagens mais novas estão aparecendo antes das mais antigas, olhem a msg do ironlynx...
renatosilva
GUJ Master

Membro desde: 16/12/2004 17:09:19
Mensagens: 1787
Offline

Pra vocês terem uma idéia, olhem o código que eu rodeii, com uma precisão de 23 casas:



Acho que confundi a mantissa do float com a parte fracionária em decimal. O 0.11001100... não é a mantissa, porque a mantissa é a parte fracionária de 1.mantissa * 2^exp = 4.8 * 10^0...

Esse era o tilte da dúvida (4) de como o lance dá mais que 4.8...agora falta entender como isso acontece...
louds
Moderador
[Avatar]

Membro desde: 29/04/2003 23:09:15
Mensagens: 4061
Localização: São Paulo
Offline

Renato, usa os métodos Float.floatToIntBits e Float.intBitsToFloat para manipular a mantissa de forma precisa.

http://www.kumpera.net/blog/
http://www.mono-project.com/
"Each individual should work for himself. People will not sacrifice themselves for the company. They come to work at the company to enjoy themselves."
Soichiro Honda
[ICQ]
renatosilva
GUJ Master

Membro desde: 16/12/2004 17:09:19
Mensagens: 1787
Offline

Não entendi como 1.6 * 3 dá mais que 4.8. Pelos meus cálculos, mesmo na forma 1.xxxx * 2^e isso dá menos, a mantissa seria 00110011001100110011001 e o resultado daria: 4,79999971389770508. O que eu fiz pra achar essa mantissa foi multiplicar 100.110011001100... por 4, o que dá 1.00110011001100..., e na mantissa de 23 bits do float portanto dá o que eu coloquei aí.

Mas usando a dica que o louds passou, vi que a mantissa é 00110011001100110011010, o que no final dá: 4,80000019073486328!

Otilde faque?
 
Índice dos Fóruns » Assuntos gerais (Off-topic)
Ir para:   
Powered by JForum 2.1.8 © JForum Team