<!-- 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) -->