Formato de ponto flutuante de precisão dupla - Double-precision floating-point format

O formato de ponto flutuante de precisão dupla (às vezes chamado de FP64 ou float64 ) é um formato de número de computador , geralmente ocupando 64 bits na memória do computador; ele representa uma ampla faixa dinâmica de valores numéricos usando um ponto base flutuante .

O ponto flutuante é usado para representar valores fracionários, ou quando um intervalo mais amplo é necessário do que o fornecido pelo ponto fixo (da mesma largura de bit), mesmo que ao custo da precisão. A precisão dupla pode ser escolhida quando o intervalo ou a precisão da precisão simples forem insuficientes.

No IEEE 754-2008 padrão , o formato de 64 bits de base 2 é oficialmente designado por binary64 ; era chamado de duplo no IEEE 754-1985 . IEEE 754 especifica formatos de ponto flutuante adicionais, incluindo precisão única de base 2 de 32 bits e, mais recentemente, representações de base 10.

Uma das primeiras linguagens de programação a fornecer tipos de dados de ponto flutuante de precisão única e dupla foi Fortran . Antes da adoção generalizada do IEEE 754-1985, a representação e as propriedades dos tipos de dados de ponto flutuante dependiam do fabricante e do modelo do computador e de decisões tomadas pelos implementadores da linguagem de programação. Por exemplo, o tipo de dados de precisão dupla do GW-BASIC era o formato de ponto flutuante MBF de 64 bits .

Formato de ponto flutuante binário de precisão dupla IEEE 754: binary64

O ponto flutuante binário de precisão dupla é um formato comumente usado em PCs, devido ao seu alcance mais amplo em relação ao ponto flutuante de precisão simples, apesar de seu desempenho e custo de largura de banda. É comumente conhecido simplesmente como duplo . O padrão IEEE 754 especifica um binary64 como tendo:

O bit de sinal determina o sinal do número (incluindo quando esse número é zero, que é assinado ).

O campo expoente é um inteiro sem sinal de 11 bits de 0 a 2047, em forma tendenciosa : um valor expoente de 1023 representa o zero real. Os expoentes variam de −1022 a +1023 porque os expoentes de −1023 (todos os 0s) e +1024 (todos os 1s) são reservados para números especiais.

A precisão do significando de 53 bits fornece de 15 a 17 dígitos decimais significativos (2 −53  ≈ 1,11 × 10 −16 ). Se uma string decimal com no máximo 15 dígitos significativos for convertida para representação de precisão dupla IEEE 754 e, em seguida, convertida de volta para uma string decimal com o mesmo número de dígitos, o resultado final deve corresponder à string original. Se um número de precisão dupla IEEE 754 for convertido em uma string decimal com pelo menos 17 dígitos significativos e, em seguida, convertido de volta para uma representação de precisão dupla, o resultado final deve corresponder ao número original.

O formato é escrito com o significando tendo um bit inteiro implícito de valor 1 (exceto para dados especiais, consulte a codificação do expoente abaixo). Com os 52 bits do significando da fração (F) aparecendo no formato de memória, a precisão total é, portanto, 53 bits (aproximadamente 16 dígitos decimais, 53 log 10 (2) ≈ 15,955). Os bits são dispostos da seguinte forma:

Formato de ponto flutuante duplo IEEE 754.svg

O valor real assumido por um dado dado de precisão dupla de 64 bits com um determinado expoente tendencioso e uma fração de 52 bits é

ou

Entre 2 52 = 4.503.599.627.370.496 e 2 53 = 9.007.199.254.740.992, os números representáveis ​​são exatamente os inteiros. Para o próximo intervalo, de 2 53 a 2 54 , tudo é multiplicado por 2, então os números representáveis ​​são pares, etc. Por outro lado, para o intervalo anterior de 2 51 a 2 52 , o espaçamento é 0,5, etc.

O espaçamento como uma fração dos números no intervalo de 2 n a 2 n +1 é 2 n −52 . O erro de arredondamento relativo máximo ao arredondar um número para o mais próximo representável (o épsilon da máquina ) é, portanto, 2 −53 .

A largura do expoente 11 bit permite a representação de números entre 10 -308 e 10 308 , com total precisão dígitos 15-17 decimal. Ao comprometer a precisão, a representação subnormal permite valores ainda menores, até cerca de 5 × 10 −324 .

Codificação de expoente

O expoente de ponto flutuante binário de precisão dupla é codificado usando uma representação binária de deslocamento , com o deslocamento de zero sendo 1023; também conhecido como viés de expoente no padrão IEEE 754. Exemplos de tais representações seriam:

e = = = 1: 00000000001200116 (menor expoente para números normais )
e = = = 1023: 0111111111123ff16 (deslocamento zero)
e = = = 1029: 10000000101240516
e = = = 2046: 1111111111027fe16 (expoente mais alto)

Os expoentes e têm um significado especial: 000167ff16

  • 000000000002= é usado para representar um zero com sinal (se F = 0) e subnormais (se F ≠ 0); e00016
  • 111111111112= é usado para representar (se F = 0) e NaNs (se F ≠ 0),7ff16

onde F é a parte fracionária do significando . Todos os padrões de bits têm codificação válida.

Exceto pelas exceções acima, todo o número de precisão dupla é descrito por:

No caso de subnormais ( e = 0), o número de dupla precisão é descrito por:

Endianness

Embora os onipresentes processadores x86 de hoje usem armazenamento little-endian para todos os tipos de dados (inteiro, ponto flutuante), há uma série de arquiteturas de hardware onde os números de ponto flutuante são representados na forma big-endian enquanto os inteiros são representados em little- forma endian. Existem processadores ARM que têm representação de ponto flutuante metade little-endian, metade big-endian para números de precisão dupla: ambas as palavras de 32 bits são armazenadas em little-endian como registradores inteiros, mas o mais significativo primeiro. Como existem muitos formatos de ponto flutuante sem representação padrão de " rede " para eles, o padrão XDR usa IEEE 754 big-endian como sua representação. Portanto, pode parecer estranho que o padrão de ponto flutuante IEEE 754 difundido não especifique endianness. Teoricamente, isso significa que mesmo os dados de ponto flutuante IEEE padrão gravados por uma máquina podem não ser lidos por outra. No entanto, em computadores padrão modernos (ou seja, implementando IEEE 754), pode-se, na prática, assumir com segurança que o endianness é o mesmo para números de ponto flutuante e inteiros, tornando a conversão direta, independentemente do tipo de dados. (Pequenos sistemas embarcados que usam formatos especiais de ponto flutuante podem ser outra questão, no entanto.)

O ponto flutuante VAX armazena palavras little-endian de 16 bits na ordem big-endian.

Exemplos de dupla precisão

0 01111111111 00000000000000000000000000000000000000000000000000000000 2 ≙ 3FF0 0000 0000 0000 0000 16 ≙ +2 0 × 1 = 1
0 01111111111 00000000000000000000000000000000000000000000000000000001 2 ≙ 3FF0 0000 0000 0001 16 ≙ +2 0 × (1 + 2 −52 ) ≈ 1.0000000000000002, o menor número> 1
0 01111111111 00000000000000000000000000000000000000000000000000000010 2 ≙ 3FF0 0000 0000 0002 16 ≙ +2 0 × (1 + 2 −51 ) ≈ 1.0000000000000004
0 10000000000 00000000000000000000000000000000000000000000000000000000 2 ≙ 4000 0000 0000 0000 16 ≙ +2 1 × 1 = 2
1 10000000000 00000000000000000000000000000000000000000000000000000000 2 ≙ C000 0000 0000 0000 16 ≙ −2 1 × 1 = −2
0 10000000000 10000000000000000000000000000000000000000000000000000000 2 ≙ 4008 0000 0000 0000 16 ≙ +2 1 × 1,1 2 = 11 2 = 3
0 10000000001 00000000000000000000000000000000000000000000000000000000 2 ≙ 4010 0000 0000 0000 16 ≙ +2 2 × 1 = 100 2 = 4
0 10000000001 01000000000000000000000000000000000000000000000000000000 2 ≙ 4014 0000 0000 0000 16 ≙ +2 2 × 1,01 2 = 101 2 = 5
0 10000000001 10000000000000000000000000000000000000000000000000000000 2 ≙ 4018 0000 0000 0000 16 ≙ +2 2 × 1,1 2 = 110 2 = 6
0 10000000011 01110000000000000000000000000000000000000000000000000000 2 ≙ 4037 0000 0000 0000 16 ≙ +2 4 × 1,0111 2 = 10111 2 = 23
0 01111111000 10000000000000000000000000000000000000000000000000000000 2 ≙ 3F88 0000 0000 0000 16 ≙ +2 −7 × 1,1 2 = 0,00000011 2 = 0,01171875 (3/256)
0 00000000000 00000000000000000000000000000000000000000000000000000001 2 ≙ 0000 0000 0000 0001 16 ≙ +2 −1022 × 2 −52 = 2 −1074 ≈ 4,9406564584124654 × 10 −324 (Min. Duplo positivo subnormal)
0 00000000000 1111111111111111111111111111111111111111111111111111 2 ≙ 000F FFFF FFFF FFFF 16 ≙ 2 -1022 x (1 - 2 -52 ) ≈ 2,2250738585072009 × 10 -308 (Max subnormal dupla.)
0 00000000001 0000000000000000000000000000000000000000000000000000 2 ≙ 0010 0000 0000 0000 16 ≙ 2 -1022 × 1 ≈ 2,2250738585072014 × 10 -308 (Min. Normais positivo duplo)
0 11111111110 11111111111111111111111111111111111111111111111111111111 2 ≙ 7FEF FFFF FFFF FFFF 16 ≙ +2 1023 × (1 + (1 - 2 −52 )) ≈ 1,7976931348623157 × 10 308 (Duplo máximo) 0 11111111110 1111111111111111111111111111111111111111111111111111111111111111111111111111111111
0 00000000000 00000000000000000000000000000000000000000000000000000000 2 ≙ 0000 0000 0000 0000 16 ≙ +0
1 00000000000 0000000000000000000000000000000000000000000000000000 2 ≙ 8000 0000 0000 0000 16 ≙ −0
0 11111111111 00000000000000000000000000000000000000000000000000000000 2 ≙ 7FF0 0000 0000 0000 16 ≙ + ∞ (infinito positivo)
1 11111111111 00000000000000000000000000000000000000000000000000000000 2 ≙ FFF0 0000 0000 0000 16 ≙ −∞ (infinito negativo)
0 11111111111 000000000000000000000000000000000000000000000000000000000001 2 ≙ 7FF0 0000 0000 0001 16 ≙ NaN (sNaN na maioria dos processadores, como x86 e ARM)
0 11111111111 10000000000000000000000000000000000000000000000000000001 2 ≙ 7FF8 0000 0000 0001 16 ≙ NaN (qNaN na maioria dos processadores, como x86 e ARM)
0 11111111111 11111111111111111111111111111111111111111111111111111111 2 ≙ 7FFF FFFF FFFF FFFF 16 ≙ NaN (uma codificação alternativa de NaN)
0 01111111101 0101010101010101010101010101010101010101010101010101 2 = 3FD5 5555 5555 5555 16 ≙ 2 -2 × (1 + 2 -2 + 2 -4 + ... + 2 -52 ) ≈ 1 / 3
0 10000000000 1001001000011111101101010100010001000010110100011000 2 = 4009 21FB 5444 2D18 16 ≈ pi

Codificações de qNaN e sNaN não são completamente especificadas no IEEE 754 e dependem do processador. A maioria dos processadores, como a família x86 e os processadores da família ARM , usa o bit mais significativo do campo de significando para indicar um NaN silencioso; isso é o que é recomendado pelo IEEE 754. Os processadores PA-RISC usam o bit para indicar um NaN de sinalização.

Por padrão, 1 / 3 rodadas para baixo, em vez de se como única precisão , devido ao número ímpar de bits do significando.

Em mais detalhes:

Given the hexadecimal representation 3FD5 5555 5555 555516,
  Sign = 0
  Exponent = 3FD16 = 1021
  Exponent Bias = 1023 (constant value; see above)
  Fraction = 5 5555 5555 555516
  Value = 2(Exponent − Exponent Bias) × 1.Fraction – Note that Fraction must not be converted to decimal here
        = 2−2 × (15 5555 5555 555516 × 2−52)
        = 2−54 × 15 5555 5555 555516
        = 0.333333333333333314829616256247390992939472198486328125
        ≈ 1/3

Velocidade de execução com aritmética de dupla precisão

Usar variáveis ​​de ponto flutuante de precisão dupla e funções matemáticas (por exemplo, sin, cos, atan2, log, exp e sqrt) é mais lento do que trabalhar com suas contrapartes de precisão única. Uma área da computação em que esse é um problema específico é o código paralelo em execução em GPUs. Por exemplo, ao usar a plataforma CUDA da NVIDIA , os cálculos com precisão dupla levam, dependendo do hardware, aproximadamente 2 a 32 vezes mais tempo para serem concluídos em comparação com aqueles feitos com precisão única .

Limitações de precisão em valores inteiros

  • Inteiros de −2 53 a 2 53 (−9.007.199.254.740.992 a 9.007.199.254.740.992) podem ser representados exatamente
  • Inteiros entre 2 53 e 2 54 = 18.014.398.509.481.984 arredondados para um múltiplo de 2 (número par)
  • Inteiros entre 2 54 e 2 55 = 36.028.797.018.963.968 arredondados para um múltiplo de 4

Implementações

Doubles são implementados em muitas linguagens de programação de maneiras diferentes, como a seguir. Em processadores apenas com precisão dinâmica, como x86 sem SSE2 (ou quando SSE2 não é usado, para fins de compatibilidade) e com precisão estendida usada por padrão, o software pode ter dificuldades para cumprir alguns requisitos.

C e C ++

C e C ++ oferecem uma ampla variedade de tipos aritméticos . A precisão dupla não é exigida pelos padrões (exceto pelo anexo opcional F de C99 , cobrindo a aritmética IEEE 754), mas na maioria dos sistemas, o doubletipo corresponde à precisão dupla. No entanto, no x86 de 32 bits com precisão estendida por padrão, alguns compiladores podem não estar em conformidade com o padrão C ou a aritmética pode sofrer de arredondamento duplo .

Fortran

O Fortran fornece vários tipos inteiros e reais, e o tipo de 64 bits real64, acessível através do módulo intrínseco do Fortran iso_fortran_env, corresponde à precisão dupla.

Lisp Comum

Common Lisp fornece os tipos SHORT-FLOAT, SINGLE-FLOAT, DOUBLE-FLOAT e LONG-FLOAT. A maioria das implementações fornece SINGLE-FLOATs e DOUBLE-FLOATs com os outros tipos de sinônimos apropriados. Common Lisp fornece exceções para capturar underflows e overflows de ponto flutuante, e a exceção de ponto flutuante inexata, de acordo com IEEE 754. Nenhum infinito e NaNs são descritos no padrão ANSI, no entanto, várias implementações os fornecem como extensões.

Java

No Java antes da versão 1.2, toda implementação precisava ser compatível com IEEE 754. A versão 1.2 permitiu implementações para trazer precisão extra em cálculos intermediários para plataformas como x87 . Assim, um modificador strictfp foi introduzido para impor cálculos IEEE 754 restritos. O ponto flutuante estrito foi restaurado em Java 17.

JavaScript

Conforme especificado pelo padrão ECMAScript , toda a aritmética em JavaScript deve ser feita usando aritmética de ponto flutuante de precisão dupla.

Veja também

  • IEEE 754 , padrão IEEE para aritmética de ponto flutuante

Notas e referências