Resumo
Os números com casas decimais de um programa não são os números reais da matemática: são um subconjunto finito de racionais cujo denominador é uma potência de dois, e todo valor que não pertence a esse subconjunto é substituído pelo mais próximo que pertence. Esta aula estuda como um número de ponto flutuante é guardado em 64 bits (sinal, expoente e mantissa), por que não tem representação exata e que valor ocupa o seu lugar, quantos dígitos são seguros em float e em double, como se acumulam os erros de arredondamento, por que dois reais não se comparam com == e como se comparam com uma tolerância justificada, e o que são o infinito e o valor não numérico que o formato reserva para os resultados que não cabem ou não existem.
Objetivos de aprendizagem
- Explicar por que não tem representação binária exata e que valor se guarda no seu lugar.
- Prever o efeito do arredondamento em somas repetidas e na soma de números de magnitudes muito diferentes.
- Comparar números reais com uma tolerância adequada em vez de
==, justificando a escolha da tolerância. - Escolher entre
floatedoublesegundo a precisão requerida, e reconhecer o infinito e o valor não numérico.
Notação científica em base 2
A aula Os inteiros e sua representação mostrou que um inteiro de bits representa exatamente valores consecutivos. Ora, os números com casas decimais apresentam uma dificuldade diferente: entre dois reais quaisquer há infinitos outros, e nenhum número finito de bits pode distingui-los todos. A solução que todos os computadores atuais adotam é a mesma que a ciência usa para escrever magnitudes muito grandes ou muito pequenas: a notação científica. Em base 10, a distância média da Terra ao Sol escreve-se metros; o número tem um sinal, uns poucos dígitos significativos (a mantissa, ) e uma potência da base (o expoente, ), que diz onde vai a vírgula. Se isto é assim, com um número fixo de dígitos cobrem-se magnitudes de ordens muito diferentes, ao preço de guardar apenas os primeiros dígitos de cada uma.
O computador faz o mesmo em base 2. Todo número diferente de zero escreve-se de uma única maneira como
onde é o sinal (0 para positivo, 1 para negativo), é a mantissa escrita em binário, cujo primeiro dígito é sempre 1, e é o expoente. Que o primeiro dígito seja sempre 1 tem uma consequência prática: não é preciso guardá-lo, e o bit que se poupa ganha-se para a precisão. O nome ponto flutuante descreve justamente esta forma: o ponto não ocupa um lugar fixo entre os dígitos, mas “flutua” conforme o expoente.
O tipo double de C++ usa, em todos os compiladores vigentes, o formato binário de precisão dupla normalizado internacionalmente, de 64 bits: 1 bit para o sinal, 11 bits para o expoente e 52 bits para a mantissa, sem o dígito inicial. O expoente é guardado deslocado: o campo de 11 bits contém , de modo que guarda valores sem sinal de 1 a 2046 para expoentes de a (os valores 0 e 2047 do campo reservam-se para o zero, os números muito pequenos, o infinito e o valor não numérico, que se verão no final da aula). O tipo float usa o formato de precisão simples, de 32 bits: 1 de sinal, 8 de expoente e 23 de mantissa.
Os bits de um double podem ser vistos. O programa seguinte usa std::bit_cast, do cabeçalho <bit>, que reinterpreta os 64 bits do número como um inteiro sem sinal de 64 bits, sem mudar nenhum; aqui ele é usado sem mais explicações, como um instrumento para olhar a memória. A especificação {:064b} escreve esse inteiro em binário com 64 dígitos, como na aula anterior, e {:016X} em hexadecimal com 16.
#include <bit>#include <cstdint>#include <print>int main(){ const double tenth{0.1}; // Os 64 bits do número, lidos como um inteiro sem sinal. const std::uint64_t bits{std::bit_cast<std::uint64_t>(tenth)}; std::println("{:064b}", bits); std::println("{:016X}", bits);}00111111101110011001100110011001100110011001100110011001100110103FB999999999999AA figura agrupa os 64 bits que o programa escreveu e os lê campo por campo.
Demonstração
Os 64 bits, em hexadecimal
3FB999999999999A. Leem-se da esquerda para a direita: 1 bit de sinal, 11 de expoente e 52 de mantissa.O sinal é 0: o número é positivo.
O campo do expoente, em binário, vale 1019; subtrai-se dele o deslocamento, 1023, e o expoente é .
A mantissa, com o 1 inicial que não se guarda, é em binário: o grupo repete-se e o último grupo, , está arredondado para cima. Seu valor é aproximadamente .
O número guardado é , isto é, : muito perto de , mas não .
Carregando o conteúdo…