Float exato [RESOLVIDO]

1. Float exato [RESOLVIDO]

???
gokernel

(usa Linux Mint)

Enviado em 10/03/2013 - 11:59h


Olá pessoal !!!

Estou desenvolvendo uma API que utiliza ( float ) ... mas quando passo um parametro o resultado não é exatamente o esperado:

no exemplo o resultado seria esse: 100.550003
o objetivo seria esse: 100.550000

DETALHE: eu nao tenho o controle se utilizar uma API externa tipo a OpenGL, pois o controle é no proprio codigo da GL.



#include <stdio.h>

void test_float (float a)
{
printf ("test_float: %f\n", a);
}

int main (int argc, char *argv[])
{
// saida:
//
// test_float: 100.550003
//
test_float ( 100.55 );

return 0;
}



Alguém teria uma dica para sanar essa pequano "problema" ?

Espero que tenha sido entendido ...



  


2. MELHOR RESPOSTA

Luis R. C. Silva
luisrcs

(usa Linux Mint)

Enviado em 10/03/2013 - 12:21h

Coloquei double e deu certo. Ficou exato.

3. Re: Float exato [RESOLVIDO]

Luis R. C. Silva
luisrcs

(usa Linux Mint)

Enviado em 10/03/2013 - 12:08h

Interessante, por que eu coloquei 1000.55 e ele retornou 1000.549988

Será erro do compilador? Impossível.


4. Re: Float exato [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 11/03/2013 - 03:55h

Usar double só muda a precisão, mas não o fato de que o valor não consegue ser devidamente representado. Se você aumentar o número de algarismos significativos na saída do printf(), isso vai ficar bem evidente.

Nos nossos computadores digitais, os números são representados usando codificação binária -- isto é: a base de numeração 2, o que significa que só há dois algarismos (0 e 1), e que cada casa à esquerda vale duas vezes o valor da casa à direita.

Números inteiros podem ser escritos usando uma soma ds potências não-negativas de 2. Por exemplo, o número 153 pode ser decomposto da seguinte forma: (1*2^7)+(0*2^6)+(0*2^5)+(1*2^4)+(1*2^3)+(0*2^2)+(0*2^1)+(1*2^0) ou, resumidamente, em notação binária: 10011001(2). Aliás, pensando por analogia, o mesmo vale para nossa represnetação decimal: 153 é uma notação mais curta para (1*10^2)+(5*10^1)+(3*10^0). (Na minha época, valor absoluto de um algarismo e o valor relativo que ele assume, dependendo da posição em que é escrito no numeral, era matéria de 4ª série, e sua generalização, usando bases de numeração diferentes de 10, matéria de 7ª ou 8ª).

O C não possui uma notação para base binária, mas possui duas que facilitam o agrupamento de bits: octal (base 8), que usa o prefixo 0 e algarismos de 0 a 7, agrupando os bits de três em três, e hexadecimal (base 16), que usa o prefixo 0x, e algarismos de 0 a 9 mais A a F (para os valores de dez a quinze). agrupando os bits de quatro em quatro.

Os tipos nativos de inteiros têm ainda outra característica em C: eles são representados usando uma quantidade fixa de bits, o que implica que existe um valor máximo representável nativamente. Para representar valores maiores do que esses, é necessároi fazer alguma composição usando vários inteiros nativos, e fia a cargo do usuário cuidar dessa composição e da forma de representá-la e interpretá-la (ainda que ele se valha de alguma biblioteca preexistente).

Algo semelhante vale para a representação de valores fracionados. A diferença em relação à representação de inteiros é que as potências de 2 usadas como parcelas na representação do número podem ter expoentes negativos. Assim sendo, 5,75, por exemplo, poderia ser pensado como (1*2^2)+(0*2^1)+(1*2^0)+(1*2^(-1))+(1*2^(-2)). Numa notação binária fracionada, poderia ser escrito como 101,11(2).

O problema para nós, que estamos acostumados a escrever números fracionados em notação decimal, é que o que parece muito simples em decimal pode virar uma dízima periódica em base 2. Por exemplo, 0,2(10), equivalente a 2/10 ou 1/5 , vira a dízima periódica 0,0011001100110011001100110011...(2), que requereria infinitos bits para poder ser exatamente representada. Como ninguém gostaria de gastar toda a memória do computador com bits de dízimas -- até porque aliás, mesmo gastando toda a memória, o número continuaria sem uma representação exata --, o que se faz é limitar a precisão e, na hora de exibir o número para o usuário, usar algum esquema de arredondamento do número.

Também isso remete a coisas que já vimos nos tempos de colégio. Uma fração como 2/3 produz a dízima periódica 0,66666666... quando escrita em notação decimal (matéria de 5ª ou 6ª séries, se não me engano). Se quisermos limitar a quantidade de algarismos significativos (início do segundo grau/ensino médio) a, por exemplo, 5 dígitos, teremos de arredondar para 0,66666 ou 0,66667, nenhum dos dois uma representação exata do núemro (lembrando que zero a esquerda não conta como algarismo significativo).

A forma mais comum de representar números fracionados em computadores digitais é a representação em ponto flutuante (em Inglês, floating point, de onde deriva o nome do tipo float do C). Ela é análoga, usando base 2, de outra coisa que se vê no primeiro ano do segundo grau: a notação científica, que permite escrever de modo uniforme números muito grandes ou muito pequenos, na forma M·10^n, onde M é um número decimal tal que 1<=M<10 e com uma quantidade determinada de algarismos significativos, e n é um expoente inteiro, usado de modo a fazer com que o valor de M, compreendido entre 1 e 10, volte à faixa original. Usando notação científica, o número 1234567 poderia ser escrito como 1,234567·10^7, e 0,000321 ficaria como 3,21·10^(-4). Em C, Pascal, BASIC, Perl, Python e outras linguagens de programação de uso comum no Ocidente, o termo "·10^" é representado somente com a letra "E" ou "e", de modo que os dois exemplos anteriores são expressos, respectivamente, como 1,234567E6 (ou 1,234567E+6[/i]) e 3,21E-4.

A representação binária em ponto flutuante escreve os valores na forma [+-]M·2^n. A parte M, chamada de mantissa, guarda um valor maior ou igual a 1 e menor do que 2 (ou, depedendo da implementação, maior ou igual a 1/2 e menor do que 1). Tanto M quanto n são representados usando uma quantidade fixa de bits, e é isso que limita a quantidade de algarismos siginificativos da mantissa e os maiores e menores números representáveis.

Apesar de frequentemente se dizer que float e double servem para variáveis que vão armazenar valores reais, na verdade valores reais em geral não podem ser armazenados de forem maiores do que um limite máximo ou menores do que um limite mínimo, e somente de modo aproximado se tais valores forem irracionais ou se forem racionais mas com um denominador que não seja uma potência de 2.

Nos nossos PCs, celulares e tablets, a representação interna de números em ponto flutuante segue o padrão IEEE-754, de 1985. Recomendo ler a respeito (na Wikipedia em Inglês, por exemplo, há uma introdução razoável). Houve uma revisão do padrão em 2008, que acrescentou, entre outras coisas, números de ponto flutuantes representados em decimal. Poucos processadores, por enquanto, têm suporte nativo a essa nova versão do padrão.


5. Re: Float exato [RESOLVIDO]

Luis R. C. Silva
luisrcs

(usa Linux Mint)

Enviado em 11/03/2013 - 22:39h

O artigo do nosso amigo sobre números gigantes não poderia ser adaptado para funcionar com números fracionários gigantes?

http://www.vivaolinux.com.br/artigo/Programacao-com-numeros-inteiros-gigantes/?pagina=1


6. Re: Float exato [RESOLVIDO]

???
gokernel

(usa Linux Mint)

Enviado em 12/03/2013 - 19:31h


Grato pessoal por participar !!!




7. Re: Float exato [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 12/03/2013 - 21:50h

Acho curioso que se coloque como melhor resposta uma resposta tecnicamente errada. 100,55 nunca terá uma representação exata enquanto tal representação for binária.

De qualquer forma, recomendo a vocês que estudem o assunto da representação em ponto flutuante (em especial a principal especificação/implementação, que é a IEEE-754 de 1985). O assunto em si é interessante, e tem vários desdobramentos importantes, incluindo a compreensão sobre a propagação e amplificação de erros de arredondamento na medida em que se opera com esses números, incluindo técnicas para minorar tais erros. Tais assuntos costumam ocupar os primeiros capítulos de livros da disciplina de Cálculo Numérico.






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts