introducao_float.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <!--
  2. Introdução à Programação - 2017 - Prof. Leoônidas de Oliveira Brandão
  3. Introdução aos números em ponto flutuantes
  4. LInE (Laboratory of Informatics in Education) - http://www.usp.br/line
  5. IME - USP
  6. Material didático
  7. Pode usar livrevemente este material para fins não comerciais, devendo sempre fazer referência à autoria.
  8. Sugestões/apontamento são bem vindos: leo@ime.usp.br (favor indicar no assunto "material de introducao 'a programacao")
  9. Prof. Leônidas de Oliveira Brandão
  10. http://www.ime.usp.br/~leo
  11. http://line.ime.usp.br
  12. http://www.matemtica.br
  13. href="#" id="url1" onclick="trocaPagina('introducao_inteiros.html')"
  14. -->
  15. <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
  16. <meta name='keywords' content='mac0122, material, professores, leonidas de oliveira brandao'>
  17. <link rel='stylesheet' type='text/css' href='css_img_js_conf/all.css'>
  18. <link rel='stylesheet' type='text/css' href='css_img_js_conf/line_introducao_programacao.css'>
  19. <script src="css_img_js_conf/defineLInE.js"></script> <!-- para referencias 'a documentos internos -->
  20. <div class="pagina">
  21. <p class="secao">Introdução aos números em ponto flutuantes</p>
  22. <p>
  23. Nesta seção examinaremos resumidamente como o computador representa números reais.
  24. A seção seguinte foi redigida para aqueles que desejam conhecer mais, estudando-a pode-se compreender a matemática associada
  25. ao conteito de representação de números em um computador digital e a razão do termo <b>ponto flutuante</b>.
  26. </p>
  27. <p>
  28. Normalmente deve-se utilizar um número fixo de <i>bits</i> para representar cada número.
  29. Por exemplo, se determinado computador usar apenas 2 <i>bytes</i> (ou 16 <i>bits</i>) para
  30. representar um real e convencionar-se que o primeiro <i>byte</i> armazena a parte inteira e o segundo
  31. <i>byte</i> a parte decimal, teríamos uma variabilidade pequena de números.
  32. Como visto no texto
  33. <!-- a href="introducao_inteiros.html" title="examinar o texto sobre inteiros">introdutório sobre inteiros</a -->
  34. <a href="#" onclick="trocaPagina('introducao_inteiros.html')" title="examinar o texto sobre inteiros">introdutório sobre inteiros</a>,
  35. com 8 <i>bits</i>, teríamos (simplificadamente) desde o <i>11111111</i> (primeiro <i>bit</i> indica negativo) até o
  36. <i>01111111</i>, que em decimal seriam desde <i>-127</i> até o <i>127</i>, pois
  37. <center>
  38. <i>
  39. 1111111<sub>2</sub> =
  40. 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> =
  41. 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255 &nbsp; &nbsp; (1)
  42. </i>
  43. </center>
  44. <!--
  45. na verdade, usando a técnica prática de <i>complemento de dois</i>, iria desde <i>-128</i> até o <i>127</i>
  46. -->
  47. </p>
  48. <center><p>[
  49. <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> |
  50. <a href="#pontoflutuante" title="Estudar os fundamentos do uso de ponto flutuante para representar reais">Ponto flutuante (PF)</a> |
  51. <a href="#pontoflutuanteSimples" title="se estiver interessado em uma explicação super simplificada, começe por aqui">PF super simplificado</a> |
  52. <a href="#exemplo" title="Um exmplo simplificado da representação em ponto flutuante">Exemplo</a> |
  53. <a href="#erro" title="Erros numéricos devido à representação em ponto flutuante">Erros</a> |
  54. <a href="#io" title="Exemplos para impressões mais interessantes de ponto flutuante">Entrada/saída</a></a>
  55. ]</p>
  56. </center>
  57. <p>
  58. <a name="pontofixo"></a>
  59. </p>
  60. <span style="color: #0055AA">Um primeiro modelo (ineficiente) para representar valores reais: ponto fixo</span>
  61. <p>
  62. 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>.
  63. Vamos continuar usando 2 <i>bytes</i> (logo 16 <i>bits</i>) e considerar 3 agrupamentos:
  64. 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);
  65. P2. os próximos 7 como a parte "decimal" (após vírgula decimal); e
  66. P3. os últimos 8 <i>bits</i> para o valor inteiro do número.
  67. </p>
  68. <p>
  69. Assim, para obter o maior valor positivo que poderia ser representado nesta notação, deveríamos deixar o primeiro <i>bit</i> em zero
  70. (para ser positivo) e todos os seguintes "ligados": <tt>0 1111111 11111111</tt> (deixei espaço em branco para indicar os 3 agrupamentos).
  71. <br/>
  72. Assim, a parte "decimal" P2 (<tt>1111111</tt>) corresponde em decimal:
  73. <center>
  74. <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> =
  75. 0.5 + 0.25 + 0.125 + 0.0625 + 0.03125 + 0.015625 + 0.007812 = 0.992188</i> &nbsp; &nbsp; (2)
  76. </center><br/>
  77. Portanto o maior positivo, convertendo para decimal, seria a soma de (1) e (2): &nbsp; <i>255.992188</i>. Desse modo, em <b>ponto fixo</b>:
  78. <br/>
  79. <center>
  80. <i>Tab. 1. Maior e menor positivo que pode-se obter com a representação em <i>ponto fixo</i>
  81. <br/>
  82. <table><tr><td>
  83. &nbsp; &nbsp; &nbsp; - maior valor positivo em binário <tt>00111111 01111111<sub>2</sub></tt> = <i>255.992188</i> em decimal;<br/>
  84. &nbsp; &nbsp; &nbsp; - menor valor estritamente positivo em binário <tt>00000001 00000000<sub>2</sub></tt> = <i>0.007812</i> em decimal.
  85. </td></tr></table>
  86. </center>
  87. </p>
  88. <p>
  89. <a name="pontoflutuante"></a>
  90. <span style="color: #0055AA">Um modelo de representação para real mais eficiente: ponto flutuante</span>
  91. </p>
  92. <p>
  93. Uma alternativa mais eficiente e amplamente utilizada na prática é a representação em <b style="color: #0000aa;">ponto flutuante</b>.
  94. 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
  95. o <b style="color: #0000aa;">expoente</b> <i>e</i>
  96. 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>.
  97. Se a mantissa tiver <i>p</i> dígitos, o número é:
  98. <tt>s x d<sub>0</sub>d<sub>1</sub>...d<sub>p-1</sub> x b<sup>e</sup></tt>.
  99. </p>
  100. <p><center>
  101. <img src="img/img_real_ponto_fixo.png" title="Imagem representando a memória com representação de ponto fixo"/>
  102. <br/>
  103. <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.
  104. </i>
  105. </center></p>
  106. <p>
  107. 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.
  108. Para poder comparar ambas as representações, vamos novamente supor um computador com reais usando apenas 2 <i>bytes</i>.
  109. </p>
  110. <p>
  111. Desse modo, o <i>maior real positivo</i> nesse <i>ponto flutuante</i> é obtido usando a maior potência e maior mantissa possiveis, respectivamente
  112. <i>0111111</i> e <i>11111111</i>, que em decimal são,
  113. <center>
  114. 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> =
  115. 64 + 32 + 16 + 8 + 4 + 2 + 1 = 127 &nbsp; &nbsp; (3)
  116. <br/>
  117. 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> =
  118. 256 + 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 511 &nbsp; &nbsp; (4)
  119. </center>
  120. <br/>
  121. Portanto, o <i>maior real positivo</i> nesse <i>ponto flutuante</i> é <i>511 x 2<sup>127</sup></i>, um número gigantesto
  122. (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>).
  123. <!--
  124. 2^127 = 170 141 183 460 469 231 731 687 303 715 884 105 728
  125. 1.70141183 * 10^38 = 170 141 183 000 000 002 920 328 542 032 742 055 936.000000
  126. 1.70141184 * 10^38 = 170 141 183 999 999 980 188 862 898 952 269 201 408.000000
  127. -->
  128. </p>
  129. <p>
  130. De forma análoga, podemos computar o <i>menor valor estritamente positivo</i> que conseguimos com essa representação.
  131. 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
  132. <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>.
  133. Convertendo <i title="-2^6">1111111</i> para decimal, temos o valor <i>-64</i>, logo o menor positivo é:
  134. <i>2<sup>-64</sup></i> que é aproximadamente <i>5.42101086243e-20</i> (tremendamente pequeno).
  135. </p>
  136. <p>
  137. Portanto o maior positivo e o menor , convertendo para decimal, seria a soma de (1) e (2): &nbsp; <i>255.992188</i>. Desse modo, em <b>ponto fixo</b>:
  138. <center>
  139. <i>Tab. 2. Maior e menor positivo que pode-se obter com a representação em <i>ponto flutuante</i>
  140. <br/>
  141. <table><tr><td>
  142. &nbsp; &nbsp; &nbsp; - maior valor positivo em binário <tt>00111111 01111111<sub>2</sub></tt> > <i>1.70141183 * 10<sup>38</sup></i> em decimal;<br/>
  143. &nbsp; &nbsp; &nbsp; - menor valor estritamente positivo em binário <tt>1111111 00000001<sub>2</sub></tt> ~=
  144. <i title="notação científica: 3e-20 equivale a 3*(2.7183^-20)">5.42101086243e-20</i> em decimal.
  145. </td></tr></table>
  146. </center>
  147. </p>
  148. <p>
  149. Comparando os resultados de <i>ponto flutuante</i> com <i>ponto fixo</i>, tabelas 1 e 2, percebemo que
  150. <i>ponto flutuante</i> produz resultados muito melhores em termos de capacidade de "simular" os números reais.
  151. </p>
  152. <p>
  153. <a name="pontoflutuantesimples"><span style="color: #0050A0">Representação em ponto flutuante</span></a>
  154. </p>
  155. <p>
  156. Esta técnica de representação é a mais utilizada nos computadores, ela utiliza um <em>bit</em> para sinal,
  157. uma quantidade pequena de <em>bits</em> para o expoente e uma quantidade maior de <em>bits</em> para a parte "principal" do
  158. 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
  159. 52 <em>bits</em> para a mantissa.
  160. </p>
  161. <p>
  162. <a name="exemplo"><span style="color: #0050A0">Exemplo de representação em ponto flutuante</span></a>
  163. </p>
  164. <p>
  165. Para ilustrar o funcionamento de ponto flutuante, suponha um computador que use representação em decimal
  166. (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
  167. teríamos <tt>p=3</tt> e <tt>-4<u><</u>e<u><</u>4</tt>.
  168. </p>
  169. <p>
  170. Deste modo, o maior valor real que pode ser representado seria o <tt>999000</tt>, pois tomando a maior mantissa e maior
  171. expoente, teríamos <tt>10<sup>3</sup> x 999 = 999000</tt>.
  172. </p>
  173. <p>
  174. 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),
  175. teríamos <tt>10<sup>-3</sup> x 1 = 0.001</tt>.
  176. </p>
  177. <p>
  178. Desse modo, nesse computador simples, a variação de números "reais" que poderiam ser representados nesse computador, seria:
  179. <center>
  180. <i>Tab. 3. Maior e menor positivo que pode-se obter com a representação em <i>ponto flutuante</i> em decimal.
  181. <br/>
  182. <table><tr><td>
  183. &nbsp; &nbsp; &nbsp; - maior valor positivo: <i>999 x 10<sup>3</sup> = 999000<i>;<br/>
  184. &nbsp; &nbsp; &nbsp; - menor valor estritamente positivo: <i>1 x 10<sup>-3</sup> = 0.001<i>.
  185. </td></tr></table>
  186. </center>
  187. </p>
  188. <p>
  189. <a name="erro"><span style="color: #0050A0">Erros numéricos</span></a>
  190. </p>
  191. <!--
  192. que corresponde ao decimal <i>31=2<sup>5</sup>-1</i>, portanto o menor real positivo
  193. 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>.
  194. -->
  195. <p>
  196. 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.
  197. Por exemplo, mesmo em notação decimal o valor 1/3 não será representado perfeitamente, ele será registrado com algo semelhante
  198. a <tt>0.3333</tt> (se o computador utilizar 4 dígitos).
  199. </p>
  200. <p>
  201. Um exemplo do problema numérico está ilustrado no exemplo abaixo, quando tentamos imprimir as somas de <tt>0.1</tt>
  202. 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á
  203. que o algoritmo nunca parará! Isso mesmo, laço infinito, pois como o computador utiliza notação binário e como o decimal
  204. <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 é
  205. <tt>0.3...</tt>).
  206. <center>
  207. <i>Tab. 4. Cuidado com a aritmética de ponto flutuante! Nem tudo é o que parece ser...</i>
  208. <br/>
  209. <table class="tbCodeLinCol">
  210. <tr><th>C </th> <th>Python</th></tr>
  211. <tr valign="top"><td><table class="tbCode">
  212. <tr><td><pre>x = 0.1;
  213. while (x!=1.0) {
  214. <verd>printf</verd>("%f\n", x);
  215. x += 0.1;
  216. }
  217. <verd>printf</verd>("Final!\n");</pre></td></tr>
  218. </table></td>
  219. <td><table class="tbCode"><pre>x = 0.1
  220. while (x!=1.0) :
  221. <verd>print</verd>(x)
  222. x += 0.1
  223. <verd>print</verd>("Final!")</pre></td></tr>
  224. </table></td></tr>
  225. </table>
  226. </center>
  227. </p>
  228. <p>
  229. <a name="io"><span style="color: #0050A0">Entrada e saída de flutuantes em <i>C</i> e em <i>Python</i></span></a>
  230. </p>
  231. <p>
  232. 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
  233. em ponto flutuante, enquanto em <i>Python</i> deve-se utilizar a função <tt>float(...)</tt>, como indicado no exemplo
  234. abaixo.
  235. <center>
  236. <i>Tab. 5. Como imprimir números em ponto flutuante.</i>
  237. <br/>
  238. <table class="tbCodeLinCol">
  239. <tr><th>C </th> <th>Python</th></tr>
  240. <tr valign="top"><td><table class="tbCode">
  241. <tr><td><pre><verm>float</verm> x; <cyan>// declaracao de variavel em "ponto flutuante"</cyan>
  242. <verd>scanf</verd>("%f", &x); <cyan>// leia como "ponto flutuante"</cyan>
  243. <verd>printf</verd>("%f\n", x); <cyan>// imprima como "ponto flutuante"</cyan>
  244. <cyan>// Usar formatador %N.kf para usar N posicoes ajustadas 'a direita e k decimais</cyan>
  245. <verd>printf</verd>("Tab.\nx=%3.2f\nx=%3.2f", x, x); <cyan>// imprima com 2 casas decimais</cyan>
  246. </pre></td></tr>
  247. </table></td>
  248. <td><table class="tbCode"><pre>x = <verm>float</verm>(<verd>input</verd>()) <cyan># leia e transforme em "ponto flutuante"</cyan>
  249. <verd>print</verd>(x); <cyan># impressao simples</cyan>
  250. <cyan># Util para colocar frases mais "sofisticadas"</cyan>
  251. <verd>print</verd>("x=%f - frases mais \"sofisticadas\"" % x); <cyan># impressoes mais sofisticadas</cyan>
  252. <cyan># Assim e' util para construir tabelas.</cyan>
  253. <verd>print</verd>("Tab.\nx=%3.2f\nx=%3.2f" % (x,x)); <cyan># o \n quebra linha</cyan>
  254. </pre></td></tr>
  255. </table></td></tr>
  256. </table></center>
  257. </p>
  258. <p>
  259. Aos alunos estudando com a linguagem <i>C</i>, tomem um cuidado adicional: o compilador <i>C</i> aceita utilizar o formatador
  260. 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>
  261. 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>.
  262. </p>
  263. <p>
  264. Uma boa fonte para pesquisar é
  265. examinar o texto na <a href="https://en.wikipedia.org/wiki/Floating-point_arithmetic" title="examinar WikiPedia: floating-point">WikiPedia: floating-point</a>.
  266. <!-- https://en.wikipedia.org/wiki/Single-precision_floating-point_format -->
  267. </p>
  268. <p class="autoria">
  269. <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/>
  270. <a href="http://www.ime.usp.br/~leo" target="_blank" title="seguir para a página do LInE">http://line.ime.usp.br</a>
  271. </p>
  272. <p class="rodape">
  273. <b>Alterações</b>:<br/>
  274. 2020/08/10: novas seções iniciais;<br/>
  275. 2020/04/18: inserido nomes nas tabelas, correcao tab. 1 ("x+=1.0"->"x+=0.1")<br/>
  276. 2017/04/14: primeira versão
  277. </p>
  278. </div>
  279. <script src="css_img_js_conf/defineLInE.js"></script> <!-- function defineEndereco (url1) -->