Ao longo desta série, discutimos como converter, operar e manipular números inteiros em diferentes bases. Vimos que, para inteiros, a conversão é sempre exata: um número inteiro em uma base sempre tem um correspondente exato em outra.
No entanto, o mundo real não é feito apenas de inteiros. Medidas, probabilidades e constantes físicas dependem de números fracionários (reais). A questão é: como representamos \(0.1\) ou \(3.14159\) usando apenas zeros e uns?
Neste artigo, expandiremos nossos métodos para tratar a parte fracionária. Você verá que, embora a lógica seja similar, as frações trazem um novo desafio: a precisão. Diferente dos inteiros, nem toda fração finita no decimal possui uma representação finita em binário, o que dá origem aos famosos erros de arredondamento na computação.
A regra geral continua a mesma: usamos a base decimal como ponte.
- Decimal \(\to\) Base \(r\): Usamos Multiplicações Sucessivas.
- Base \(s\) \(\to\) Decimal: Usamos Expansão Posicional (com expoentes negativos).
De base decimal para qualquer base (Multiplicações Sucessivas) #
Para converter a parte fracionária do decimal para binário, octal ou hexadecimal, usamos o método das multiplicações sucessivas. Diferente da parte inteira (onde dividíamos), aqui multiplicamos pela base de destino para “empurrar” os dígitos para a parte inteira.
O Algoritmo:
- Multiplique a parte fracionária do número pela base de destino \(r\).
- A parte inteira do resultado torna-se o próximo dígito da nova base.
- Pegue apenas a parte fracionária restante e repita o processo.
- Pare quando o resultado for zero (exato) ou quando atingir a precisão desejada (no caso de dízimas).
Exemplo: Converter \(0.625_{10}\) para binário (\(2\)) com 3 casas decimais
- \(0.625 \times 2 = \mathbf{1}.25\) → guardamos 1, sobra \(0.25\)
- \(0.25 \times 2 = \mathbf{0}.50\) → guardamos 0, sobra \(0.50\)
- \(0.50 \times 2 = \mathbf{1}.00\) → guardamos 1, sobra \(0.00\)
Como a fração chegou a zero, a conversão é exata. Lemos de cima para baixo:
$$ 0.625_{10} = 0.101_2 $$O grande desafio aqui são as frações que parecem simples em decimal, mas são infinitas (periódicas) em binário. Veja o caso do \(0.3\):
Exemplo: Converter \(0.3_{10}\) para binário (\(2\)) com 5 casas decimais
- \(0.3 \times 2 = \mathbf{0}.6\) → guardamos 0
- \(0.6 \times 2 = \mathbf{1}.2\) → guardamos 1
- \(0.2 \times 2 = \mathbf{0}.4\) → guardamos 0
- \(0.4 \times 2 = \mathbf{0}.8\) → guardamos 0
- \(0.8 \times 2 = \mathbf{1}.6\) → guardamos 1
Note que sobrou \(0.6\), o que nos levaria de volta ao passo 2. O resultado é uma dízima periódica (\(0.010011001...\)). Para obter as 5 casas solicitadas no enunciado do exemplo, truncamos:
$$ 0.3_{10} \approx 0.01001_2 $$E quando temos parte Inteira e Fracionária juntas? #
A estratégia é o clássico “dividir para conquistar”. Você não precisa de um método novo; basta separar o número em dois problemas menores:
- Converta a parte inteira usando Divisões Sucessivas (como visto em artigo anterior).
- Converta a parte fracionária usando Multiplicações Sucessivas.
- Junte os dois resultados com o ponto (ou vírgula) separador.
Exemplo Misto: Converter \(6.512_{10}\) para binário (\(2\)) com 5 casas de precisão
Parte 1: O Inteiro (\(6\)) Usamos divisões:
$$ 6 \div 2 = 3 \quad (\text{resto } \mathbf{0}) \\ 3 \div 2 = 1 \quad (\text{resto } \mathbf{1}) \\ 1 \div 2 = 0 \quad (\text{resto } \mathbf{1}) $$Resultado Inteiro: \(110_2\)
Parte 2: A Fração (\(0.512\)) Usamos multiplicações:
- \(0.512 \times 2 = \mathbf{1}.024\) → guardamos 1
- \(0.024 \times 2 = \mathbf{0}.048\) → guardamos 0
- \(0.048 \times 2 = \mathbf{0}.096\) → guardamos 0
- \(0.096 \times 2 = \mathbf{0}.192\) → guardamos 0
- \(0.192 \times 2 = \mathbf{0}.384\) → guardamos 0
Resultado Fracionário: \(0.10000..._2\)
Parte 3: União
$$ 6.512_{10} \approx 110.10000_2 $$Visualizando no Python (O Raio-X da Memória) #
Vamos usar o Python para ver como números reais são armazenados na memória do computador. Lembre-se que, internamente, os números reais são representados em ponto flutuante, seguindo o padrão IEEE 754.
O padrão IEEE 754 será detalhado em artigos futuros. Mas o básico para você precisar saber agora é que um número real em ponto flutuante é dividido em três campos principais:
- Sinal (1 bit): Indica se o número é positivo (0) ou negativo (1).
- Expoente (8 bits para float de 32 bits): Armazena o expoente “ajustado” (com viés). O viés para floats de 32 bits é 127.
- Mantissa (23 bits para float de 32 bits): Armazena a parte fracionária do número, com um “bit escondido” implícito.
Aqui estamos usando 32 bits apenas como exemplo. Para outros tamanhos de ponto flutuante (como double de 64 bits), os tamanhos dos campos são diferentes, mas o conceito é o mesmo. Novamente, isso será aprofundado em artigos futuros. Agora, vamos focar em como visualizar esses bits no Python.
Talvez sua primeira reação seja tentar usar a função nativa bin(), que usamos
exaustivamente nos artigos sobre números inteiros. Mas se você tentar rodar
bin(0.625), o Python lhe devolverá um erro:
TypeError: 'float' object cannot be interpreted as an integer
Isso acontece porque a função bin() foi feita para mostrar a representação
exata de inteiros. Para ver como um número real (float) está realmente
gravado nos 32 bits da memória — enxergando os campos de Sinal, Expoente e
Mantissa — precisamos usar uma abordagem diferente.
Vamos usar a biblioteca struct para pegar o valor float, empacotá-lo em bytes
brutos e depois ler esses mesmos bytes como se fossem um inteiro. Isso nos dá um
“Raio-X” do hardware. A biblioteca struct é utilizada para converter entre
representações Python e C, permitindo manipular dados binários diretamente.
O código a seguir usa o formato 'f' do struct, que empacota o número em
32 bits (o float da linguagem C). Lembre-se que o Python armazena seus
floats com 64 bits ('d'), usando 11 bits de expoente e 52 de mantissa. A
estrutura de sinal, expoente e mantissa é idêntica; apenas os tamanhos dos
campos diferem.
import struct
def ver_bits_reais(numero):
# 'f' = float (32 bits padrão IEEE 754)
# 'I' = unsigned int (inteiro sem sinal de 32 bits)
# pack: converte o número float para bytes brutos
# unpack: lê esses bytes brutos como se fossem um inteiro
bits_inteiros = struct.unpack('I', struct.pack('f', numero))[0]
# Formata como uma string de 32 zeros e uns
bits_string = f"{bits_inteiros:032b}"
# Vamos imprimir de forma didática, separando os campos
sinal = bits_string[0]
expoente = bits_string[1:9]
mantissa = bits_string[9:]
print(f"\nAnalisando o número: {numero}")
print(f"Bits Brutos: {bits_string}")
print(f"Divisão: [S:{sinal}] [Exp:{expoente}] [Mant:{mantissa}]")
# Teste 1: Número apenas fracionário (Exemplo anterior)
ver_bits_reais(0.625)
# Teste 2: Número com parte inteira e fracionária
ver_bits_reais(12.5)
# Teste 3: Número periódico (0.1)
ver_bits_reais(0.1)
# Teste 4: Número negativo (-3.75)
ver_bits_reais(-3.75)Resultado da execução:
Analisando o número: 0.625
Bits Brutos: 00111111001000000000000000000000
Divisão: [S:0] [Exp:01111110] [Mant:01000000000000000000000]
Analisando o número: 12.5
Bits Brutos: 01000001010010000000000000000000
Divisão: [S:0] [Exp:10000010] [Mant:10010000000000000000000]
Analisando o número: 0.1
Bits Brutos: 00111101110011001100110011001101
Divisão: [S:0] [Exp:01111011] [Mant:10011001100110011001101]
Analisando o número: -3.75
Bits Brutos: 11000000011100000000000000000000
Divisão: [S:1] [Exp:10000000] [Mant:11100000000000000000000]O que estamos vendo?
Para entender as contas a seguir, considere que multiplicar por \(2^e\) é o mesmo que mover a vírgula \(e\) casas para a direita (se \(e\) for positivo) ou para a esquerda (se \(e\) for negativo).
- No caso do 0.625:
- O expoente é
01111110(126 decimal). Como o viés é 127, o expoente real é \(126 - 127 = -1\). - A mantissa guardada é
01000.... Lembre-se do “Bit Escondido”: o computador assume um “1.” na frente. Então a mantissa real é \(1.01\). - Conta final: \(1.01_2 \times 2^{-1} = 0.101_2 = 0.625\). Bateu!
- No caso do 12.5:
- O expoente é
10000010(130 decimal). Exponente real: \(130 - 127 = 3\). - A mantissa guardada é
1001000.... Colocando o “1.” implícito, temos \(1.1001\). - Conta final: \(1.1001_2 \times 2^3\). Mover a vírgula 3 casas para a direita nos dá \(1100.1_2\), que é exatamente \(12.5\) decimal.
- No caso do 0.1 (O Revelador):
- O expoente é
01111011(123 decimal). Expoente real: \(123 - 127 = -4\). - Olhe para a mantissa:
100110011001.... Você percebe o padrão repetitivo? Aqui está a prova visual de que 0.1 é uma dízima periódica em binário. - Note o último bit
1. O computador precisou arredondar o valor porque a sequência era infinita. É essa pequena “sujeira” no final que causa erros como \(0.1 + 0.2 \neq 0.3\). - Conta final: \(1.100110011001..._2 \times 2^{-4}\). Mover a vírgula 4 casas para a esquerda nos dá \(0.0001100110011..._2\), que é a representação binária aproximada de \(0.1\).
- No caso do -3.75:
- Sinal:
1(Negativo). - O expoente é
10000000(128 decimal). Expoente real: \(128 - 127 = 1\). Isso significa multiplicar por \(2^1\). - A mantissa guardada é
111000.... Adicionando o “1.” implícito, temos \(1.111\). - Conta final: \(-1 \times 1.111_2 \times 2^1\). Mover a vírgula 1 casa para a direita resulta em \(-11.11_2\).
- Traduzindo: \(11_2\) é 3 e \(0.11_2\) é \(0.5 + 0.25 = 0.75\). Resultado: \(-3.75\).
Caso tenha tido dificuldade, não se preocupe. A representação em ponto flutuante é um tema complexo e profundo. Por isso mesmo, dedicamos o próximo artigo desta série exclusivamente ao padrão IEEE 754, onde faremos um mergulho detalhado em cada campo — sinal, expoente e mantissa — e explicaremos os casos especiais como NaN, Infinito e números Subnormais.
De qualquer base para Decimal (Expansão Posicional) #
Para saber o valor decimal de uma fração em outra base, usamos a expansão posicional. A diferença é que, após a vírgula, as posições têm expoentes negativos: \(b^{-1}, b^{-2}, b^{-3}\), e assim por diante.
$$ 0.d_1 d_2 d_3 \dots _b = d_1 \cdot b^{-1} + d_2 \cdot b^{-2} + d_3 \cdot b^{-3} + \dots $$
Exemplo: Converter \(0.101_2\) para decimal (\(10\))
A conversão é exata, pois a soma é finita.
O Caso Geral: Usando a Ponte Decimal #
Se precisamos converter entre duas bases quaisquer entre si (ex: Base 6 para Octal), o caminho mais seguro é:
- Converter a fração de origem para Decimal (Expansão).
- Converter do Decimal para Destino (Multiplicação).
Exemplo: Converter \(0.32_4\) para binário (\(2\)) com 3 casas decimais
-
Converter Base 4 para Decimal:
$$ \begin{align*} 0.32_4 & = 3 \times 4^{-1} + 2 \times 4^{-2} \\ & = 3 \times 0.25 + 2 \times 0.0625 = 0.75 + 0.125 = 0.875_{10} \end{align*} $$ -
Converter Decimal (\(0.875\)) para Binário:
- \(0.875 \times 2 = \mathbf{1}.75\) → dígito 1
- \(0.75 \times 2 = \mathbf{1}.50\) → dígito 1
- \(0.50 \times 2 = \mathbf{1}.00\) → dígito 1
Veja que a fração chegou a zero, então a conversão é exata.
Resultado final:
$$ 0.32_4 = 0.111_2 $$
Exemplo: Converter \(0.52_6\) para octal (\(8\)) com 3 casas decimais
-
Converter Base 6 para Decimal:
$$ \begin{align*} 0.52_6 & = 5 \times 6^{-1} + 2 \times 6^{-2} \\ & = 5 \times 0.1666... + 2 \times 0.0277... \approx 0.8888_{10} \end{align*} $$(Nota: Para precisão máxima, mantenha as frações: \(5/6 + 2/36 = 32/36 = 8/9\))
-
Converter Decimal (\(8/9 \approx 0.888...\)) para Octal:
- \(0.8888... \times 8 = 7.1111...\) → dígito 7
- \(0.1111... \times 8 = 0.8888...\) → dígito 0
- \(0.8888... \times 8 = 7.1111...\) → dígito 7
Observe que o padrão se repete, indicando uma dízima periódica em octal.
Resultado aproximado:
$$ 0.52_6 \approx 0.707_8 $$Bases Relacionadas por Potência (Atalhos) #
Assim como nos inteiros, se as bases são potências uma da outra (como 2, 8 e 16), podemos converter agrupando ou expandindo bits.
O Agrupamento Parte da Vírgula
Para números mistos (que têm parte inteira e fracionária), a regra de ouro é: comece sempre pela vírgula e vá para as extremidades.
Imagine a vírgula como um espelho. O agrupamento deve “se afastar” dela em ambas as direções:
- Parte Inteira (Esquerda da vírgula): Agrupe da direita para a esquerda \(\leftarrow\). Se o último grupo ficar incompleto, adicione zeros à esquerda.
- Parte Fracionária (Direita da vírgula): Agrupe da esquerda para a direita \(\rightarrow\). Se o último grupo ficar incompleto, adicione zeros à direita.
Exemplo: Converter \(0.A7_{16}\) para binário (\(2\))
Expandimos cada dígito hexadecimal da parte fracionária em 4 bits:
$$ A_{16} = 1010_2, \quad 7_{16} = 0111_2 $$Resultado:
$$ 0.A7_{16} = 0.10100111_2 $$Agora, veja um exemplo onde o preenchimento de zeros é necessário.
Exemplo: Converter \(11011.11_2\) para Hexadecimal (\(16\))
O ponto de partida é a vírgula. Vamos agrupar 4 bits em cada direção.
$$ \leftarrow \mathbf{11011} \quad . \quad \mathbf{11} \rightarrow $$Lado Esquerdo (Inteiro):
- Grupo 1 (perto da vírgula): \(1011\) \(\to B\)
- Grupo 2 (ponta esquerda): Sobrou \(1\). Completamos com zeros à esquerda: \(0001\) \(\to 1\)
Lado Direito (Fração):
- Grupo 1 (perto da vírgula): Temos apenas \(11\). Como precisamos de 4 bits, completamos com zeros à direita: \(1100\) \(\to C\)
Resultado Final:
$$ 1B.C_{16} $$Mais um exemplo para reforçar o conceito:
Novo Exemplo: Converter \(0.11011_2\) para Hexadecimal (\(16\))
Precisamos agrupar os bits em blocos de 4, partindo da vírgula para a direita.
Número original:
$$ 0.11011_2 $$Grupo 1: Os primeiros 4 bits:
$$ 1101_2 = D_{16} $$Grupo 2: Sobrou apenas o bit 1. Como precisamos de um bloco de 4, completamos com zeros à direita:
(Nota: Se completássemos à esquerda, teríamos \(0001=1\), o que estaria errado, pois o bit original tem peso \(2^{-5}\), não \(2^{-8}\))
Resultado final:
$$ 0.11011_2 = 0.D8_{16} $$Ponto Flutuante em Python: Curiosidades e Limitações #
Além do que vimos anteriormente na seção “Visualizando no Python (O Raio-X da Memória)”, há algumas outras curiosidades interessantes sobre como o Python lida com números reais e suas bases.
Diferente dos números inteiros, onde o Python nos dá prefixos fáceis como 0b e
0x, lidar com bases não-decimais para números fracionários exige ferramentas
diferentes.
Não existe “Literal Binário Fracionário” #
Você não pode escrever algo como x = 0b1.101 em Python. A linguagem não
suporta essa sintaxe nativamente.
Se você precisar converter uma string binária fracionária (como “10.11”) para float, terá que fazer isso manualmente ou usar bibliotecas. A lógica interna do computador é binária, mas o Python abstrai isso para o programador.
Floats Hexadecimais #
Pouca gente sabe, mas o Python permite converter floats para hexadecimal e vice-versa de forma nativa. Isso é extremamente útil para programadores que precisam saber exatamente o que está salvo na memória, sem os arredondamentos da visualização decimal.
O formato usa a notação 0x...p..., onde p indica uma potência de 2
(semelhante à notação científica, mas binária).
# Convertendo float para sua representação exata em Hex
x = 10.5
print(x.hex())
# Saída: '0x1.5000000000000p+3'
# Significa: 1.5 (hexa) * 2^3
# Convertendo de volta (Hex para Float)
y = float.fromhex('0x0.3p10') # 0.1875 * 1024
print(y)
# Saída: 192.0Se você achou essa saída confusa, não se preocupe. Essa notação (float.hex)
parece uma “salada de bases” porque ela mistura Base 16 com potências de
Base 2.
Vamos detalhar mais a notação. Ela segue o formato: 0x[Mantissa Hexadecimal]p[Expoente Decimal]
A regra de ouro para decifrar isso é:
$$ \text{Valor} = (\text{Número Hexadecimal}) \times 2^{(\text{Expoente após o p})} $$Vamos dissecar os dois exemplos acima para entender a matemática por trás:
1. Analisando 0x1.5p+3 (que resultou em 10.5)
- A Mantissa (
1.5): Está em Hexadecimal. O1vale 1, mas o5depois do ponto não é “5 décimos”. Em base 16, cada casa decimal divide por 16.- Logo,
.5hex significa \(5/16\) ou \(0.3125\) em decimal. - Valor da Mantissa: \(1 + 0.3125 = \mathbf{1.3125}\).
- Logo,
- O Expoente (
p+3): Opindica uma potência de 2.- Valor do Expoente: \(2^3 = \mathbf{8}\).
- Conta Final: \(1.3125 \times 8 = \mathbf{10.5}\).
2. Analisando 0x0.3p10 (que resultou em 192.0)
- A Mantissa (
0.3): Novamente, em Hexadecimal.- O
.3hex significa \(3/16\) ou \(0.1875\) em decimal.
- O
- O Expoente (
p10):- Valor do Expoente: \(2^{10} = \mathbf{1024}\).
- Conta Final: \(0.1875 \times 1024 = \mathbf{192.0}\).
Por que usar isso? Essa notação é útil para programadores de sistemas porque ela define o número exatamente como ele é armazenado no hardware, sem sofrer os erros de arredondamento típicos da conversão Decimal → Binário. É a forma mais precisa de transportar números float entre diferentes máquinas.
A Prova Real do Erro de Precisão #
Lembra que discutimos como \(0.3_{10}\) vira uma dízima periódica em binário? No Python, isso gera o famoso “erro” de aritmética de ponto flutuante.
Como o computador trunca a dízima binária, a soma não bate exata:
val1 = 0.1
val2 = 0.2
soma = val1 + val2
print(f"Resultado: {soma}")
# Saída: 0.30000000000000004
print(soma == 0.3)
# Saída: FalsePara aplicações críticas (como sistemas financeiros) onde esse erro é inaceitável, o Python oferece o módulo decimal, que simula a aritmética base-10 perfeita ao custo de desempenho, evitando a conversão para binário.
from decimal import Decimal
d1 = Decimal('0.1')
d2 = Decimal('0.2')
print(d1 + d2)
# Saída: 0.3Estratégias para Contornar o Erro #
Além de usar o módulo Decimal, existem duas abordagens algorítmicas muito
comuns para lidar com essa limitação, dependendo do seu domínio de problema:
A. Trabalhar com Inteiros (Abordagem Financeira)
Em sistemas bancários ou de e-commerce, muitas vezes evitamos floats
completamente. A ideia é trabalhar com a menor unidade monetária (centavos) como
inteiro.
Ao invés de guardar R$ 10.50, guardamos o inteiro 1050. Como a matemática de inteiros é exata, o erro de arredondamento desaparece. Só dividimos por 100 na hora de exibir o valor na tela.
# Abordagem com Floats (Perigosa para dinheiro)
preco_a = 0.10
preco_b = 0.20
print(preco_a + preco_b) # 0.30000000000000004
# Abordagem com Inteiros (Segura)
centavos_a = 10
centavos_b = 20
total = centavos_a + centavos_b
print(f"R$ {total / 100:.2f}") # R$ 0.30B. Comparação por Tolerância (Abordagem Científica)
Em contextos científicos ou de engenharia, raramente verificamos se dois números
reais são estritamente iguais (==). Ao invés disso, verificamos se eles
estão “perto o suficiente” um do outro, dentro de uma margem de erro aceitável
chamada de tolerância, usualmente denotada por \(\epsilon\) (letra grega
épsilon).
Em Python, você pode fazer isso manualmente ou usar a função math.isclose:
import math
val1 = 0.1 + 0.2
esperado = 0.3
# O jeito errado
if val1 == esperado:
print("Iguais") # Nunca entra aqui
# O jeito manual (definindo uma tolerância, ex: 0.00001)
tolerancia = 1e-5
if abs(val1 - esperado) < tolerancia:
print("Iguais (dentro da margem)")
# O jeito Pythonico (recomendado)
# Aqui passamos sua variável para o 'abs_tol' (Absolute Tolerance)
if math.isclose(val1, esperado, abs_tol=tolerancia):
print(f"Iguais (usando math.isclose com tolerância de {tolerancia})")A função math.isclose é útil porque permite definir o que significa
“igual” de duas formas diferentes. Entender a diferença entre elas é vital para
evitar bugs silenciosos:
1. Tolerância Relativa (rel_tol) - O Padrão
- O que é: É uma comparação baseada em porcentagem ou magnitude. O padrão é
1e-9(0.000000001), o que significa que os números precisam ser iguais até a 9ª casa decimal significativa. - Quando usar: Na maioria dos casos gerais.
- A Lógica: Um erro de 1 metro é inaceitável se você está medindo uma mesa (erro relativo alto), mas é irrelevante se você está medindo a distância da Terra à Lua (erro relativo baixo). O
rel_tolse adapta à escala dos números.
2. Tolerância Absoluta (abs_tol) - O Valor Fixo
- O que é: É uma margem de erro fixa, como uma régua. Se você definir
abs_tol=0.01, qualquer diferença menor que 0.01 é aceita, não importa se os números são gigantes ou minúsculos. O padrão é0.0. - Quando usar: Principalmente ao comparar com Zero.
- A Lógica: Se você tentar verificar
math.isclose(0.0, 0.0000001), a tolerância relativa falhará (pois qualquer coisa comparada a zero é infinitamente maior percentualmente). Nesse caso, você precisa definir umabs_tolpara dizer: “considero zero qualquer coisa menor que \(10^{-7}\)”.
import math
# Exemplo de Tolerância Relativa (Padrão)
# 100 e 101 são "próximos"? Com 5% de tolerância, sim.
print(math.isclose(100, 101, rel_tol=0.05)) # True
# Exemplo de Tolerância Absoluta (Zero)
# Comparar com zero exige abs_tol
a = 0.0
b = 1e-10 # 0.0000000001
print(math.isclose(a, b)) # False (O padrão falha com zero)
print(math.isclose(a, b, abs_tol=1e-9)) # True (Diferença é menor que a margem fixa)A biblioteca Numpy, amplamente usada em contextos científicos, também oferece
funções para comparação com tolerância, como numpy.isclose e numpy.allclose,
que são otimizadas para arrays grandes.
Considerações Finais: O Problema da Precisão #
Ao longo deste artigo, você deve ter notado que conversões como \(0.3_{10} \to \text{Binário}\) resultam em dízimas infinitas. Como a memória do computador é finita, ele não pode armazenar “infinitos bits”. Ele precisa cortar (truncar) ou arredondar esse número em algum momento.
Isso significa que o computador quase nunca armazena exatamente o número
\(0.3\), mas sim algo muito próximo, como \(0.299999999...\) ou
\(0.30000000004\). É esse pequeno erro de arredondamento, inerente à conversão
de bases fracionárias, que causa comportamentos “estranhos” em linguagens de
programação, como 0.1 + 0.2 != 0.3.
No próximo artigo, entraremos na anatomia do padrão IEEE 754, o sistema universal que define como esses bits (sinal, expoente e mantissa) são organizados para minimizar esses erros e permitir cálculos científicos.
Até lá!