123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- <!--
- Introdução à Programação - 2017 - Prof. Leoônidas de Oliveira Brandão
- Introdução aos números em ponto flutuantes
- LInE (Laboratory of Informatics in Education) - http://www.usp.br/line
- IME - USP
- Material didático
- Pode usar livrevemente este material para fins não comerciais, devendo sempre fazer referência à autoria.
- Sugestões/apontamento são bem vindos: leo@ime.usp.br (favor indicar no assunto "material de introducao 'a programacao")
- Prof. Leônidas de Oliveira Brandão
- http://www.ime.usp.br/~leo
- http://line.ime.usp.br
- http://www.matemtica.br
- href="#" id="url1" onclick="trocaPagina('introducao_inteiros.html')"
- -->
- <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
- <meta name='keywords' content='mac0122, material, professores, leonidas de oliveira brandao'>
- <link rel='stylesheet' type='text/css' href='css_img_js_conf/all.css'>
- <link rel='stylesheet' type='text/css' href='css_img_js_conf/line_introducao_programacao.css'>
- <script src="css_img_js_conf/defineLInE.js"></script> <!-- para referencias 'a documentos internos -->
- <div class="pagina">
- <p class="secao">Introdução aos números em ponto flutuantes</p>
- <p>
- Nesta seção examinaremos resumidamente como o computador representa números reais.
- A seção seguinte foi redigida para aqueles que desejam conhecer mais, estudando-a pode-se compreender a matemática associada
- ao conteito de representação de números em um computador digital e a razão do termo <b>ponto flutuante</b>.
- </p>
- <p>
- Normalmente deve-se utilizar um número fixo de <i>bits</i> para representar cada número.
- Por exemplo, se determinado computador usar apenas 2 <i>bytes</i> (ou 16 <i>bits</i>) para
- representar um real e convencionar-se que o primeiro <i>byte</i> armazena a parte inteira e o segundo
- <i>byte</i> a parte decimal, teríamos uma variabilidade pequena de números.
- Como visto no texto
- <!-- a href="introducao_inteiros.html" title="examinar o texto sobre inteiros">introdutório sobre inteiros</a -->
- <a href="#" onclick="trocaPagina('introducao_inteiros.html')" title="examinar o texto sobre inteiros">introdutório sobre inteiros</a>,
- com 8 <i>bits</i>, teríamos (simplificadamente) desde o <i>11111111</i> (primeiro <i>bit</i> indica negativo) até o
- <i>01111111</i>, que em decimal seriam desde <i>-127</i> até o <i>127</i>, pois
- <center>
- <i>
- 1111111<sub>2</sub> =
- 2<sup>7</sup> + 2<sup>6</sup> + 2<sup>5</sup> + 2<sup>4</sup> + 2<sup>3</sup> + 2<sup>2</sup> + 2<sup>1</sup> + 2<sup>0</sup> =
- 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255 (1)
- </i>
- </center>
- <!--
- na verdade, usando a técnica prática de <i>complemento de dois</i>, iria desde <i>-128</i> até o <i>127</i>
- -->
- </p>
- <center><p>[
- <a href="#pontofixo" title="Estudar este outro modelo de representação de real para entender a razão do uso de ponto flutuante">Ponto fixo</a> |
- <a href="#pontoflutuante" title="Estudar os fundamentos do uso de ponto flutuante para representar reais">Ponto flutuante (PF)</a> |
- <a href="#pontoflutuanteSimples" title="se estiver interessado em uma explicação super simplificada, começe por aqui">PF super simplificado</a> |
- <a href="#exemplo" title="Um exmplo simplificado da representação em ponto flutuante">Exemplo</a> |
- <a href="#erro" title="Erros numéricos devido à representação em ponto flutuante">Erros</a> |
- <a href="#io" title="Exemplos para impressões mais interessantes de ponto flutuante">Entrada/saída</a></a>
- ]</p>
- </center>
- <p>
- <a name="pontofixo"></a>
- </p>
- <span style="color: #0055AA">Um primeiro modelo (ineficiente) para representar valores reais: ponto fixo</span>
- <p>
- Para apresentar o conceito que interessa, de <i>ponto flutuante</i>, apresentarei sua contra-parte, como seria uma representação em <b>ponto fixo</b>.
- Vamos continuar usando 2 <i>bytes</i> (logo 16 <i>bits</i>) e considerar 3 agrupamentos:
- P1. o primeiro <i>bit</i> para representar o sinal do número (<i>s=0</i> para positivo e <i>s=1</i> para negativo);
- P2. os próximos 7 como a parte "decimal" (após vírgula decimal); e
- P3. os últimos 8 <i>bits</i> para o valor inteiro do número.
- </p>
- <p>
- Assim, para obter o maior valor positivo que poderia ser representado nesta notação, deveríamos deixar o primeiro <i>bit</i> em zero
- (para ser positivo) e todos os seguintes "ligados": <tt>0 1111111 11111111</tt> (deixei espaço em branco para indicar os 3 agrupamentos).
- <br/>
- Assim, a parte "decimal" P2 (<tt>1111111</tt>) corresponde em decimal:
- <center>
- <i>2<sup>-1</sup> + 2<sup>-2</sup> + 2<sup>-3</sup> + 2<sup>-4</sup> + 2<sup>-5</sup> + 2<sup>-6</sup> + 2<sup>-7</sup> =
- 0.5 + 0.25 + 0.125 + 0.0625 + 0.03125 + 0.015625 + 0.007812 = 0.992188</i> (2)
- </center><br/>
- Portanto o maior positivo, convertendo para decimal, seria a soma de (1) e (2): <i>255.992188</i>. Desse modo, em <b>ponto fixo</b>:
- <br/>
- <center>
- <i>Tab. 1. Maior e menor positivo que pode-se obter com a representação em <i>ponto fixo</i>
- <br/>
- <table><tr><td>
- - maior valor positivo em binário <tt>00111111 01111111<sub>2</sub></tt> = <i>255.992188</i> em decimal;<br/>
- - menor valor estritamente positivo em binário <tt>00000001 00000000<sub>2</sub></tt> = <i>0.007812</i> em decimal.
- </td></tr></table>
- </center>
- </p>
-
- <p>
- <a name="pontoflutuante"></a>
- <span style="color: #0055AA">Um modelo de representação para real mais eficiente: ponto flutuante</span>
- </p>
- <p>
- Uma alternativa mais eficiente e amplamente utilizada na prática é a representação em <b style="color: #0000aa;">ponto flutuante</b>.
- Nessa representação também quebramos o número em três agrupamentos, a primeira sendo o <b>sinal</b> <i>s</i>, a segunda
- o <b style="color: #0000aa;">expoente</b> <i>e</i>
- e a terceira sendo <b style="color: #0000aa;">mantissa</b> <i>m</i>, assim o valor do número seria <i>s x m x b<sup>e</sup></i>.
- Se a mantissa tiver <i>p</i> dígitos, o número é:
- <tt>s x d<sub>0</sub>d<sub>1</sub>...d<sub>p-1</sub> x b<sup>e</sup></tt>.
- </p>
- <p><center>
- <img src="img/img_real_ponto_fixo.png" title="Imagem representando a memória com representação de ponto fixo"/>
- <br/>
- <i>Fig. 1. Representação em memória para <i>ponto fixo</i> para representar reais, com 7 <i>bits</i> de expoente e 8 <i>bits</i> para mantissa.
- </i>
- </center></p>
- <p>
- Assim, nota-se que a diferença entre a representações em <i>ponto flutuante</i> e em <i>ponto fixo</i> é o tratamento do segundo agrupamento.
- Para poder comparar ambas as representações, vamos novamente supor um computador com reais usando apenas 2 <i>bytes</i>.
- </p>
- <p>
- Desse modo, o <i>maior real positivo</i> nesse <i>ponto flutuante</i> é obtido usando a maior potência e maior mantissa possiveis, respectivamente
- <i>0111111</i> e <i>11111111</i>, que em decimal são,
- <center>
- 2<sup>6</sup> + 2<sup>5</sup> + 2<sup>4</sup> + 2<sup>3</sup> + 2<sup>2</sup> + 2<sup>1</sup> + 2<sup>0</sup> =
- 64 + 32 + 16 + 8 + 4 + 2 + 1 = 127 (3)
- <br/>
- 2<sup>8</sup> + 2<sup>7</sup> + 2<sup>6</sup> + 2<sup>5</sup> + 2<sup>4</sup> + 2<sup>3</sup> + 2<sup>2</sup> + 2<sup>1</sup> + 2<sup>0</sup> =
- 256 + 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 511 (4)
- </center>
- <br/>
- Portanto, o <i>maior real positivo</i> nesse <i>ponto flutuante</i> é <i>511 x 2<sup>127</sup></i>, um número gigantesto
- (maior que <i title="170 141 183 000 000 002 920 328 542 032 742 055 936.0">1.70141183 x 10<sup>38</sup></i>).
- <!--
- 2^127 = 170 141 183 460 469 231 731 687 303 715 884 105 728
- 1.70141183 * 10^38 = 170 141 183 000 000 002 920 328 542 032 742 055 936.000000
- 1.70141184 * 10^38 = 170 141 183 999 999 980 188 862 898 952 269 201 408.000000
- -->
- </p>
- <p>
- De forma análoga, podemos computar o <i>menor valor estritamente positivo</i> que conseguimos com essa representação.
- Como são 7 <i>bits</i> para expoente e 8 <i>bits</i> para a mantissa, devemos pegar no agrupamento P2 a sequência
- <i>1111111</i> (maior potência negativa possível) e no agrupamento P3 o menor valor positivo, que seja maior que zero, portanto <i>00000001</i>.
- Convertendo <i title="-2^6">1111111</i> para decimal, temos o valor <i>-64</i>, logo o menor positivo é:
- <i>2<sup>-64</sup></i> que é aproximadamente <i>5.42101086243e-20</i> (tremendamente pequeno).
- </p>
- <p>
- Portanto o maior positivo e o menor , convertendo para decimal, seria a soma de (1) e (2): <i>255.992188</i>. Desse modo, em <b>ponto fixo</b>:
- <center>
- <i>Tab. 2. Maior e menor positivo que pode-se obter com a representação em <i>ponto flutuante</i>
- <br/>
- <table><tr><td>
- - maior valor positivo em binário <tt>00111111 01111111<sub>2</sub></tt> > <i>1.70141183 * 10<sup>38</sup></i> em decimal;<br/>
- - menor valor estritamente positivo em binário <tt>1111111 00000001<sub>2</sub></tt> ~=
- <i title="notação científica: 3e-20 equivale a 3*(2.7183^-20)">5.42101086243e-20</i> em decimal.
- </td></tr></table>
- </center>
- </p>
- <p>
- Comparando os resultados de <i>ponto flutuante</i> com <i>ponto fixo</i>, tabelas 1 e 2, percebemo que
- <i>ponto flutuante</i> produz resultados muito melhores em termos de capacidade de "simular" os números reais.
- </p>
- <p>
- <a name="pontoflutuantesimples"><span style="color: #0050A0">Representação em ponto flutuante</span></a>
- </p>
- <p>
- Esta técnica de representação é a mais utilizada nos computadores, ela utiliza um <em>bit</em> para sinal,
- uma quantidade pequena de <em>bits</em> para o expoente e uma quantidade maior de <em>bits</em> para a parte "principal" do
- número. Em um dos padrões de representação (IEEE 754) o número é representado com 11 <em>bits</em> para o expoente e
- 52 <em>bits</em> para a mantissa.
- </p>
- <p>
- <a name="exemplo"><span style="color: #0050A0">Exemplo de representação em ponto flutuante</span></a>
- </p>
- <p>
- Para ilustrar o funcionamento de ponto flutuante, suponha um computador que use representação em decimal
- (os computadores na verdade usam base <i>binária</i>), tendo apenas 3 dígitos para mantissa e o expoente sendo de <i>-4</i> até <i>-4</i>, assim
- teríamos <tt>p=3</tt> e <tt>-4<u><</u>e<u><</u>4</tt>.
- </p>
- <p>
- Deste modo, o maior valor real que pode ser representado seria o <tt>999000</tt>, pois tomando a maior mantissa e maior
- expoente, teríamos <tt>10<sup>3</sup> x 999 = 999000</tt>.
- </p>
- <p>
- Já o menor valor estritamente positivo que conseguiríamos seria o <tt>0.001</tt>, pois tomando a menor mantissa positiva e menor expoente possível (-3),
- teríamos <tt>10<sup>-3</sup> x 1 = 0.001</tt>.
- </p>
-
- <p>
- Desse modo, nesse computador simples, a variação de números "reais" que poderiam ser representados nesse computador, seria:
- <center>
- <i>Tab. 3. Maior e menor positivo que pode-se obter com a representação em <i>ponto flutuante</i> em decimal.
- <br/>
- <table><tr><td>
- - maior valor positivo: <i>999 x 10<sup>3</sup> = 999000<i>;<br/>
- - menor valor estritamente positivo: <i>1 x 10<sup>-3</sup> = 0.001<i>.
- </td></tr></table>
- </center>
- </p>
- <p>
- <a name="erro"><span style="color: #0050A0">Erros numéricos</span></a>
- </p>
- <!--
- que corresponde ao decimal <i>31=2<sup>5</sup>-1</i>, portanto o menor real positivo
- seria <i>1 x 10<sup>-31</sup></i> que é um número muito menor que o <i>1/255</i> da representação em <i>ponto fixo</i>.
- -->
- <p>
- A primeira questão que aparece ao tratar números não inteiros no computador digital é a perda de precisão ou os erros numéricos.
- Por exemplo, mesmo em notação decimal o valor 1/3 não será representado perfeitamente, ele será registrado com algo semelhante
- a <tt>0.3333</tt> (se o computador utilizar 4 dígitos).
- </p>
- <p>
- Um exemplo do problema numérico está ilustrado no exemplo abaixo, quando tentamos imprimir as somas de <tt>0.1</tt>
- até <tt>0.9</tt>. Experimente copiar este trecho de código e roder em <em>C</em> ou em <em>Python</em>, você verá
- que o algoritmo nunca parará! Isso mesmo, laço infinito, pois como o computador utiliza notação binário e como o decimal
- <tt>0.1</tt> em binário corresponde a um binário periódico (como a dízima periódica resultante do <tt>1/3</tt> que é
- <tt>0.3...</tt>).
- <center>
- <i>Tab. 4. Cuidado com a aritmética de ponto flutuante! Nem tudo é o que parece ser...</i>
- <br/>
- <table class="tbCodeLinCol">
- <tr><th>C </th> <th>Python</th></tr>
- <tr valign="top"><td><table class="tbCode">
- <tr><td><pre>x = 0.1;
- while (x!=1.0) {
- <verd>printf</verd>("%f\n", x);
- x += 0.1;
- }
- <verd>printf</verd>("Final!\n");</pre></td></tr>
- </table></td>
- <td><table class="tbCode"><pre>x = 0.1
- while (x!=1.0) :
- <verd>print</verd>(x)
- x += 0.1
- <verd>print</verd>("Final!")</pre></td></tr>
- </table></td></tr>
- </table>
- </center>
- </p>
- <p>
- <a name="io"><span style="color: #0050A0">Entrada e saída de flutuantes em <i>C</i> e em <i>Python</i></span></a>
- </p>
- <p>
- Em <i>C</i> deve-se utilizar o formatador <tt>%f</tt> para indicar que os <em>bits</em> devem ser tratados como número
- em ponto flutuante, enquanto em <i>Python</i> deve-se utilizar a função <tt>float(...)</tt>, como indicado no exemplo
- abaixo.
- <center>
- <i>Tab. 5. Como imprimir números em ponto flutuante.</i>
- <br/>
- <table class="tbCodeLinCol">
- <tr><th>C </th> <th>Python</th></tr>
- <tr valign="top"><td><table class="tbCode">
- <tr><td><pre><verm>float</verm> x; <cyan>// declaracao de variavel em "ponto flutuante"</cyan>
- <verd>scanf</verd>("%f", &x); <cyan>// leia como "ponto flutuante"</cyan>
- <verd>printf</verd>("%f\n", x); <cyan>// imprima como "ponto flutuante"</cyan>
- <cyan>// Usar formatador %N.kf para usar N posicoes ajustadas 'a direita e k decimais</cyan>
- <verd>printf</verd>("Tab.\nx=%3.2f\nx=%3.2f", x, x); <cyan>// imprima com 2 casas decimais</cyan>
- </pre></td></tr>
- </table></td>
- <td><table class="tbCode"><pre>x = <verm>float</verm>(<verd>input</verd>()) <cyan># leia e transforme em "ponto flutuante"</cyan>
- <verd>print</verd>(x); <cyan># impressao simples</cyan>
- <cyan># Util para colocar frases mais "sofisticadas"</cyan>
- <verd>print</verd>("x=%f - frases mais \"sofisticadas\"" % x); <cyan># impressoes mais sofisticadas</cyan>
- <cyan># Assim e' util para construir tabelas.</cyan>
- <verd>print</verd>("Tab.\nx=%3.2f\nx=%3.2f" % (x,x)); <cyan># o \n quebra linha</cyan>
- </pre></td></tr>
- </table></td></tr>
- </table></center>
- </p>
- <p>
- Aos alunos estudando com a linguagem <i>C</i>, tomem um cuidado adicional: o compilador <i>C</i> aceita utilizar o formatador
- de inteiro na leitura e na impressão de uma variável em flutuante. Ele trunca o valor, assim se tiver <tt>float x=1.5;</tt>
- e fizer <tt><verd>printf</verd>("x=%d\n", x);</tt>, não haverá erro de compilação e será impresso <tt>x=1</tt>.
- </p>
- <p>
- Uma boa fonte para pesquisar é
- examinar o texto na <a href="https://en.wikipedia.org/wiki/Floating-point_arithmetic" title="examinar WikiPedia: floating-point">WikiPedia: floating-point</a>.
- <!-- https://en.wikipedia.org/wiki/Single-precision_floating-point_format -->
- </p>
- <p class="autoria">
- <a href="https://www.ime.usp.br/~leo" target="_blank" title="seguir para a pagina do prof. Leônidas">Leônidas de Oliveira Brandão</a><br/>
- <a href="http://www.ime.usp.br/~leo" target="_blank" title="seguir para a página do LInE">http://line.ime.usp.br</a>
- </p>
- <p class="rodape">
- <b>Alterações</b>:<br/>
- 2020/08/10: novas seções iniciais;<br/>
- 2020/04/18: inserido nomes nas tabelas, correcao tab. 1 ("x+=1.0"->"x+=0.1")<br/>
- 2017/04/14: primeira versão
- </p>
- </div>
- <script src="css_img_js_conf/defineLInE.js"></script> <!-- function defineEndereco (url1) -->
|