Ir para conteúdo

DBL_MIN não é o menor valor possível para double.


fredericopissarra

Posts Recomendados

Perguntaram isso hoje... Em float.h temos uma série de constantes léxicas que definem valores máximos e mínimos para vários aspectos relativos a ponto flutnate. Um deles é DBL_MIN que quem não leu a especificação ISO 9989 ou não compreende, de fato, o que é ponto flutuante, assume que seja o menor valor possível para o tipo double. Não é...

DBL_MIN é o menor valor normalizado possível. O padrão IEEE 754 define 4 "tipos" de valores para ponto flutuante: subnormais, normalizados, "não é um número" e infinitos. Geralmente queremos ficar dentro da faixa dos valores normalizados. Mas, que "norma" é essa? Lembram-se da "notação científica" da época das aulas de Física na escola? Eles são definidos como tendo um número bem definido de algarismos onde o primeiro deles, a parte inteira, é sempre escrita com um algarismo diferene de zero, segue uma potência de 10 que funciona como escala... Assim, 3002 pode ser escrito como 3.002*10³, em notação científica.... Essa "norma" não permite que a parte inteira seja zero... A mesma coisa acontece com ponto flutuante binário. O número é estruturado de acordo com a equação:

png.latex.png.ce06a90a87700f5175b31808dc703a40.png

Onde F é um inteiro de 52 bits de tamanho, E é um inteiro de 11 bits de tamanho e S tem 1 bit. Repare o 1 somado a fração. Isso significa que, na equação acima, o valor sempre tem esse 1 como valor inteiro (implícito) na estrutura do double... Mas a equação acima só é usada se 0<E<2047. Se E==0 então temos um valor subnormal, onde o 1.0, implícito, não está lá. De fato, a parte inteira é 0, neste caso e a equação muda:

png.latex2.png.771ad80f3a00a76b3a17780c1a8d03ce.png

Aqui, F continua tendo 52 bits e a fração continua sendo menor que 1, mas note que agora temos uma escala fixa. Sim, o valor 0.0 é um subnormal especial (onde F=0)... Ele é especial porque cálculos involvendo 0.0 não causam problemas de performance. Lidar com subnormais num cálculo, em ourto caso, sempre causa problemas de performance (a instrução que lida com um valor desses gasta mais tempo para terminar o processamento).

Voltando: O valor DBL_MIN geralmente é definido como 2.2250738585072014E-308 e obter esse valor é simples. Observando a equação dos doubles normalizados, se tivermos F=0 e E=1, teremos apenas 2⁻¹⁰²² como valor final:

$ bc -q
scale=350
2^-1022
.0000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000022250738585072013830902327173324\
04064219215

Mas, como disse antes, existem valores menores que esse ainda... O menor valor possível, subnormal, diferente de zero, para um double é quando F=1 e E=0, o que nos dá aproximadamente 4.9406564584124654E-324, obtido com esse pequeno programa:

#include <stdio.h>

int main( void )
{
  unsigned long long x = 1;  // F=1, E=0, S=0
  double *p = (double *)&x;

  printf( "%.16e\n", *p );
}

O mesmo valor pode ser obtido com o bc usando a equalção para valores subnormais:

$ bc -q
scale=350
(1/2^52)*2^-1022
.0000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000004940656458412465\
44176568792

Que, é claro, é a mesma coisa que 2⁻¹⁰⁷⁴.

Os valores NaN e infinitos têm E=2047 e não nos interessam no contexto deste texto...

Link para o comentário
Compartilhar em outros sites

Sobre a notação científica. Quem quer que tenha lidado com eletricidade ou eletrônica pode discordar do conceito, já que para facilitar a escrita de sub-unidades (mili, micro, pico... ou quilo, mega, giga) é costumeito usar a parte inteira com mais de um algarismo... Em cálculos é comum usar 20.0*10⁻³ A para escrever, depois, 20 mA. Mas isso não está normalizado... Ou seja, não é "notação científica"...

A "norma" é clara... o algarismo inteiro é sempre um só e sempre diferente de zero.

Link para o comentário
Compartilhar em outros sites

Outra coisa importante sobre "notação científica" e sobre ponto flutuante no padrão IEEE 754 é que o número de algarismos é FIXO... Se alguém te pede PI com 4 algarismos você tem que, obrigatóriamente escrever 3.142*10⁰ (o último algarismo têm que ser arredondado)... A mesma coisa acontece com ponto flutuante: O valor armazenado na estrutura tem número fixo de bits (24 para float, 53 para double, 64 para long double e 113 para __float128, se existir).

Isso tem uma consequência importante... O tipo float, mesmo podendo representar valores na ordem de 2¹²⁷ (ou 10³⁸), não permite o armazenamento, com precisão, mais de 24 bits... Um valor como 123456789 (da ordem de 10⁸) não pode ser armazenado de forma exata, só aproximada. Eis um exemplo:

#include <stdio.h>

int main( void )
{
  int x = 123456789;
  float y = x;

  printf( "%d -> %f\n", x, y );
}

O resultado será "123456789 -> 123456792.000000". Isso porque 123456789 (0x75BCD15) tem 27 bits de tamanho (3 a mais que cabem num float). Daí, os 3 bits inferiores são perdidos e um arredondamento é feito no 4º bit (por isso ficou maior)... Note que um int, em teoria, permite o armazenamento de valores da ordem de 10⁹, bem menos que um float, mas isso não significa que é possível armazenar o valor de um int de forma EXATA num float. Evide misturá-los, se possível.

Então, ao lidar com ponto flutuante, não somente o tipo (subnormal, normalizado, nans, infinitos) são importantes, mas também a precisão binária.

Link para o comentário
Compartilhar em outros sites

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...