Aritmética de ponto flutuante
Na computação, aritmética de ponto flutuante (FP) é uma aritmética que representa números reais aproximadamente, usando um inteiro com uma precisão fixa, chamado de significando, escalado por um expoente inteiro de uma base fixa. Por exemplo, 12,345 pode ser representado como um número de ponto flutuante de base dez:
Na prática, a maioria dos sistemas de ponto flutuante usa a base dois, embora a base dez (ponto flutuante decimal) também seja comum.
O termo ponto flutuante refere-se ao fato de que o ponto de base do número pode "flutuar" em qualquer lugar à esquerda, à direita ou entre os dígitos significativos do número. Essa posição é indicada pelo expoente, portanto, o ponto flutuante pode ser considerado uma forma de notação científica.
Um sistema de ponto flutuante pode ser usado para representar, com um número fixo de dígitos, números de ordens de grandeza muito diferentes — como o número de metros entre galáxias ou entre prótons em um átomo. Por esta razão, a aritmética de ponto flutuante é freqüentemente usada para permitir números reais muito pequenos e muito grandes que requerem tempos de processamento rápidos. O resultado dessa faixa dinâmica é que os números que podem ser representados não são espaçados uniformemente; a diferença entre dois números representáveis consecutivos varia com seu expoente.
Ao longo dos anos, várias representações de ponto flutuante foram usadas em computadores. Em 1985, o padrão IEEE 754 para aritmética de ponto flutuante foi estabelecido e, desde a década de 1990, as representações mais comumente encontradas são aquelas definidas pelo IEEE.
A velocidade das operações de ponto flutuante, comumente medida em termos de FLOPS, é uma característica importante de um sistema de computador, especialmente para aplicações que envolvem cálculos matemáticos intensivos.
Uma unidade de ponto flutuante (FPU, coloquialmente um coprocessador matemático) é parte de um sistema de computador especialmente projetado para realizar operações em números de ponto flutuante.
Visão geral
Números de ponto flutuante
Uma representação numérica especifica alguma forma de codificar um número, geralmente como uma sequência de dígitos.
Existem vários mecanismos pelos quais sequências de dígitos podem representar números. Na notação matemática padrão, a sequência de dígitos pode ter qualquer comprimento e a localização do ponto de base é indicada colocando-se um "ponto" caractere (ponto ou vírgula) ali. Se o ponto de base não for especificado, a string representa implicitamente um número inteiro e o ponto de base não declarado estaria fora da extremidade direita da string, próximo ao dígito menos significativo. Em sistemas de ponto fixo, uma posição na string é especificada para o ponto de base. Portanto, um esquema de ponto fixo pode usar uma sequência de 8 dígitos decimais com o ponto decimal no meio, em que "00012345" representaria 0001.2345.
Na notação científica, o número fornecido é escalado por uma potência de 10, de modo que fique dentro de um intervalo específico - normalmente entre 1 e 10, com o ponto de base aparecendo imediatamente após o primeiro dígito. Como uma potência de dez, o fator de escala é então indicado separadamente no final do número. Por exemplo, o período orbital da lua Io de Júpiter é 152.853,5047 segundos, um valor que seria representado em notação científica padrão como 1.528535047×105 segundos.
A representação de ponto flutuante é semelhante em conceito à notação científica. Logicamente, um número de ponto flutuante consiste em:
- Uma sequência de dígitos assinada (significando positivo ou negativo) de um determinado comprimento em uma dada base (ou radix). Esta cadeia de dígitos é chamada de significando, O que foi?ou coeficiente. O comprimento do significand determina o precisão a que os números podem ser representados. A posição do ponto radix é presumida sempre estar em algum lugar dentro do significand - muitas vezes logo após ou pouco antes do dígito mais significativo, ou à direita do dígito mais certo (mesmo significativo). Este artigo geralmente segue a convenção que o ponto radix é definido logo após o dígito mais significativo (esquerdista).
- Um expoente inteiro assinado (também referido como o característicaou escala), que modifica a magnitude do número.
Para derivar o valor do número de ponto flutuante, o significando é multiplicado pela base elevada à potência do expoente, equivalente a deslocar o ponto de base de sua posição implícita por um número de casas igual ao valor do expoente - para a direita se o expoente for positivo ou para a esquerda se o expoente for negativo.
Usando a base 10 (a conhecida notação decimal) como exemplo, o número 152.853,5047, que tem dez dígitos decimais de precisão, é representado como o significando 1.528.535.047 junto com 5 como o expoente. Para determinar o valor real, um ponto decimal é colocado após o primeiro dígito do significando e o resultado é multiplicado por 10< /span>5 para dar 1,528535047×105, ou 152.853,5047. Ao armazenar tal número, a base (10) não precisa ser armazenada, pois será a mesma para todo o intervalo de números suportados e pode, assim, ser inferida.
Simbolicamente, este valor final é:
onde s é o significando (ignorando qualquer ponto decimal implícito), p é a precisão (o número de dígitos no significando), b é a base (no nosso exemplo, este é o número dez), e e é o expoente.
Historicamente, várias bases numéricas foram usadas para representar números de ponto flutuante, com base dois (binário) sendo a mais comum, seguida pela base dez (ponto flutuante decimal), e outras variedades menos comuns, como base dezesseis (ponto flutuante hexadecimal), base oito (ponto flutuante octal), base quatro (ponto flutuante quaternário), base três (ponto flutuante ternário balanceado) e mesmo base 256 e base 65.536.
Um número de ponto flutuante é um número racional, porque pode ser representado como um inteiro dividido por outro; por exemplo 1,45× 103 é (145/100) × 1000 ou 145.000/100. A base determina as frações que podem ser representadas; por exemplo, 1/5 não pode ser representado exatamente como um número de ponto flutuante usando uma base binária, mas 1/5 pode ser representado exatamente usando uma base decimal (0,2 ou 2×10−1). No entanto, 1/3 não pode ser representado exatamente por binário (0,010101...) ou decimal (0,333...), mas na base 3 é trivial (0,1 ou 1×3−1). As ocasiões em que ocorrem expansões infinitas dependem da base e de seus fatores primos.
A forma como o significado (incluindo o seu sinal) e expoente são armazenados em um computador é dependente da implementação. Os formatos IEEE comuns são descritos em detalhes mais tarde e em outros lugares, mas como um exemplo, na representação de ponto flutuante (32-bit) da única precisão binária, , e assim o significand é uma cadeia de 24 bits. Por exemplo, os primeiros 33 bits do número π são:
Nesta expansão binária, vamos denotar as posições de 0 (bit mais à esquerda ou bit mais significativo) a 32 (bit mais à direita). O significando de 24 bits parará na posição 23, mostrada como o bit sublinhado 0 acima. O próximo bit, na posição 24, é chamado de round bit ou rounding bit. Ele é usado para arredondar a aproximação de 33 bits para o número de 24 bits mais próximo (existem regras específicas para valores intermediários, o que não é o caso aqui). Este bit, que é 1 neste exemplo, é adicionado ao inteiro formado pelos 24 bits mais à esquerda, produzindo:
Quando isso é armazenado na memória usando a codificação IEEE 754, isso se torna o significando s. Supõe-se que o significando tenha um ponto binário à direita do bit mais à esquerda. Assim, a representação binária de π é calculada da esquerda para a direita da seguinte forma:
onde p é a precisão (24 neste exemplo), n é a posição do bit do significando da esquerda (começando em 0 e terminando em 23 aqui) e e é o expoente (1 neste exemplo).
Pode ser necessário que o dígito mais significativo do significando de um número diferente de zero seja diferente de zero (exceto quando o expoente correspondente for menor que o mínimo). Este processo é chamado de normalização. Para formatos binários (que usa apenas os dígitos 0 e 1), este dígito diferente de zero é necessariamente 1. Portanto, não precisa ser representado na memória, permitindo que o formato tenha um bit a mais de precisão. Essa regra é chamada de convenção de bit principal, convenção de bit implícita, convenção de bit oculto ou convenção de bit presumido.
Alternativas para números de ponto flutuante
A representação de ponto flutuante é de longe a forma mais comum de representar em computadores uma aproximação de números reais. No entanto, existem alternativas:
- Representação de ponto fixo usa operações de hardware inteiro controladas por uma implementação de software de uma convenção específica sobre a localização do ponto binário ou decimal, por exemplo, 6 bits ou dígitos da direita. O hardware para manipular essas representações é menos caro do que o ponto flutuante, e pode ser usado para executar operações de inteiro normal, também. O ponto fixo binário é geralmente usado em aplicações especiais em processadores embarcados que só podem fazer aritmética inteira, mas o ponto fixo decimal é comum em aplicações comerciais.
- Os sistemas de números logarítmicos (LNSs) representam um número real pelo logaritmo de seu valor absoluto e um bit de sinal. A distribuição de valor é semelhante ao ponto flutuante, mas a curva de valor para representação (Ou seja., o gráfico da função logaritmo) é suave (exceto em 0). Inversamente à aritmética de ponto flutuante, em uma multiplicação de sistema de números logarítmicos, divisão e exponenciação são simples de implementar, mas a adição e subtração são complexas. A aritmética (simétrica) de índice de nível (LI e SLI) de Charles Clenshaw, Frank Olver e Peter Turner é um esquema baseado em uma representação logaritm generalizada.
- Representação de ponto flutuante cônico, que não parece ser usado na prática.
- Alguns números racionais simples (por exemplo., 1/3 e 1/10) não pode ser representado exatamente no ponto flutuante binário, não importa qual é a precisão. Usar um radix diferente permite representar alguns deles (por exemplo., 1/10 em ponto flutuante decimal), mas as possibilidades permanecem limitadas. Pacotes de software que executam aritmética racional representam números como frações com numerador integral e denominador, e podem, portanto, representar qualquer número racional exatamente. Tais pacotes geralmente precisam usar aritmética "bignum" para os inteiros individuais.
- A aritmética interval permite representar números como intervalos e obter limites garantidos sobre os resultados. É geralmente baseado em outras aritméticas, em particular ponto flutuante.
- Sistemas de álgebra de computador, como Mathematica, Maxima e Maple podem lidar com números irracionais como ou de uma forma completamente "formal" (computação simbólica), sem lidar com uma codificação específica do significadond. Tal programa pode avaliar expressões como "" exatamente, porque é programado para processar a matemática subjacente diretamente, em vez de usar valores aproximados para cada cálculo intermediário.
História
Em 1914, Leonardo Torres y Quevedo propôs uma forma de ponto flutuante no curso de discutir seu projeto para uma calculadora eletromecânica de propósito especial. Em 1938, Konrad Zuse de Berlim completou o Z1, o primeiro computador mecânico binário, programável; ele usa uma representação de números de ponto flutuante binário de 24 bits com um expoente assinado de 7 bits, um significado de 17 bits (incluindo um bit implícito), e um sinal de bit. O Z3 baseado em relés mais confiável, concluído em 1941, tem representações para infinidades positivas e negativas; em particular, implementa operações definidas com infinito, como , e para em operações indefinidas, como .
Zuse também propôs, mas não completo, aritmética de ponto flutuante cuidadosamente arredondado que inclui e representações NaN, antecipando características do padrão IEEE por quatro décadas. Em contraste, von Neumann recomendou contra números de ponto flutuante para a máquina IAS de 1951, argumentando que a aritmética de ponto fixo é preferível.
O primeiro computador comercial com hardware de ponto flutuante foi o computador Z4 de Zuse, projetado em 1942–1945. Em 1946, a Bell Laboratories apresentou o Mark V, que implementava números de ponto flutuante decimais.
O Pilot ACE possui aritmética de ponto flutuante binário e tornou-se operacional em 1950 no National Physical Laboratory, no Reino Unido. Mais tarde, trinta e três foram vendidos comercialmente como o English Electric DEUCE. A aritmética é realmente implementada em software, mas com uma taxa de clock de um megahertz, a velocidade das operações de ponto flutuante e ponto fixo nesta máquina foi inicialmente mais rápida do que a de muitos computadores concorrentes.
O IBM 704 produzido em massa veio em 1954; introduziu o uso de um expoente tendencioso. Por muitas décadas depois disso, o hardware de ponto flutuante era tipicamente um recurso opcional, e os computadores que o possuíam eram chamados de "computadores científicos" ou de "computação científica". (SC) (consulte também Extensões para Computação Científica (XSC)). Não foi até o lançamento do Intel i486 em 1989 que os computadores pessoais de uso geral tiveram capacidade de ponto flutuante no hardware como um recurso padrão.
A série UNIVAC 1100/2200, introduzida em 1962, suportava duas representações de ponto flutuante:
- Precisão única: 36 bits, organizado como um sinal de 1 bit, um expoente de 8 bits e um significand de 27 bits.
- Dupla precisão: 72 bits, organizado como um sinal de 1 bit, um expoente de 11 bits e um significand de 60 bits.
O IBM 7094, também lançado em 1962, suportava representações de precisão simples e precisão dupla, mas sem relação com as representações do UNIVAC. De fato, em 1964, a IBM introduziu representações hexadecimais de ponto flutuante em seus mainframes System/360; essas mesmas representações ainda estão disponíveis para uso em sistemas z/Architecture modernos. Em 1998, a IBM implementou a aritmética binária de ponto flutuante compatível com IEEE em seus mainframes; em 2005, a IBM também adicionou aritmética de ponto flutuante decimal compatível com IEEE.
Inicialmente, os computadores usavam muitas representações diferentes para números de ponto flutuante. A falta de padronização no nível do mainframe era um problema contínuo no início dos anos 1970 para aqueles que escreviam e mantinham o código-fonte de nível superior; esses padrões de ponto flutuante do fabricante diferiam no tamanho das palavras, nas representações, no comportamento de arredondamento e na precisão geral das operações. A compatibilidade de ponto flutuante em vários sistemas de computação precisava desesperadamente de padronização no início dos anos 80, levando à criação do padrão IEEE 754, uma vez que a palavra de 32 bits (ou 64 bits) se tornou comum. Esse padrão foi significativamente baseado em uma proposta da Intel, que estava projetando o coprocessador numérico i8087; A Motorola, que estava projetando o 68000 na mesma época, também deu uma contribuição significativa.
Em 1989, o matemático e cientista da computação William Kahan foi homenageado com o Prêmio Turing por ser o principal arquiteto por trás dessa proposta; ele foi auxiliado por seu aluno Jerome Coonen e um professor visitante, Harold Stone.
Entre as inovações do x86 estão:
- Uma representação de ponto flutuante precisamente especificada no nível de bit-string, de modo que todos os computadores compatíveis interpretem padrões de bit da mesma maneira. Isso torna possível transferir com precisão e eficiência números de ponto flutuante de um computador para outro (após a contabilidade de endianness).
- Um comportamento precisamente especificado para as operações aritméticas: Um resultado é necessário para ser produzido como se a aritmética infinitamente precisa fosse usada para produzir um valor que é então arredondado de acordo com regras específicas. Isso significa que um programa de computador compatível sempre produziria o mesmo resultado quando dado uma entrada particular, mitigando assim a reputação quase mística que a computação de ponto flutuante tinha desenvolvido para seu comportamento aparentemente não-determinístico.
- A capacidade de condições excepcionais (overflow, dividir por zero, etc.) para propagar através de uma computação de forma benigna e, em seguida, ser tratada pelo software de forma controlada.
Intervalo de números de ponto flutuante
Um número de ponto flutuante consiste em dois componentes de ponto fixo, cujo alcance depende exclusivamente do número de bits ou dígitos em sua representação. Enquanto os componentes dependem linearmente de sua faixa, a faixa de ponto flutuante depende linearmente da faixa do significando e exponencialmente da faixa do componente expoente, que atribui uma faixa extraordinariamente mais ampla ao número.
Em um sistema de computador típico, um número de ponto flutuante binário de precisão dupla (64 bits) tem um coeficiente de 53 bits (incluindo 1 bit implícito), um expoente de 11 bits e 1 bit de sinal. Como 210 = 1024, o intervalo completo dos números de ponto flutuante normais positivos neste formato é de 2−1022 ≈ 2 × 10−308 sup> para aproximadamente 21024 ≈ 2 × 10308.
O número de números normais de ponto flutuante em um sistema (B, P, L, U) onde
- B é a base do sistema,
- P é a precisão do significand (na base B),
- L é o menor expoente do sistema,
- U é o maior expoente do sistema,
o .
Há um menor número de ponto flutuante normal positivo,
- Nível de fluxo = UFL = ,
que tem 1 como dígito inicial e 0 para os dígitos restantes do significando e o menor valor possível para o expoente.
Há um maior número de ponto flutuante,
- Nível de excesso = OFL = ,
que tem B − 1 como valor para cada dígito do significando e o maior valor possível para o expoente.
Além disso, existem valores representáveis estritamente entre −UFL e UFL. Ou seja, zeros positivos e negativos, bem como números subnormais.
IEEE 754: ponto flutuante em computadores modernos
O IEEE padronizou a representação de computador para números binários de ponto flutuante no IEEE 754 (também conhecido como IEC 60559) em 1985. Esse primeiro padrão é seguido por quase todas as máquinas modernas. Ele foi revisado em 2008. Os mainframes IBM suportam o próprio formato de ponto flutuante hexadecimal da IBM e ponto flutuante decimal IEEE 754-2008, além do formato binário IEEE 754. A série Cray T90 tinha uma versão IEEE, mas o SV1 ainda usa o formato de ponto flutuante Cray.
O padrão fornece muitos formatos intimamente relacionados, diferindo apenas em alguns detalhes. Cinco desses formatos são chamados formatos básicos, e outros são denominados formatos de precisão estendida e formato de precisão extensível. Três formatos são especialmente amplamente utilizados em hardware e linguagens de computador:
- Precisão única (binary32), geralmente usada para representar o tipo "float" na família de linguagem C. Este é um formato binário que ocupa 32 bits (4 bytes) e seu significadond tem uma precisão de 24 bits (cerca de 7 dígitos decimais).
- Dupla precisão (binary64), geralmente usada para representar o tipo "duplo" na família de linguagem C. Este é um formato binário que ocupa 64 bits (8 bytes) e seu significadond tem uma precisão de 53 bits (cerca de 16 dígitos decimais).
- Duplo estendido, também ambiguously chamado "extended precision" formato. Este é um formato binário que ocupa pelo menos 79 bits (80 se a regra de bits oculta/implícita não for usada) e seu significadond tem uma precisão de pelo menos 64 bits (cerca de 19 dígitos decimais). Os padrões C99 e C11 da família de linguagem C, em seu anexo F ("IEC 60559 aritmética de ponto flutuante"), recomendam que tal formato estendido seja fornecido como "duplo longo". Um formato que satisfaz os requisitos mínimos (precisão significand de 64 bits, exponente de 15 bits, encaixando assim em 80 bits) é fornecido pela arquitetura x86. Muitas vezes em tais processadores, este formato pode ser usado com "duplo longo", embora a precisão estendida não está disponível com MSVC. Para fins de alinhamento, muitas ferramentas armazenam esse valor de 80 bits em um espaço de 96 bits ou 128 bits. Em outros processadores, "long double" pode ser um formato maior, como precisão quádrupla, ou apenas precisão dupla, se qualquer forma de precisão estendida não estiver disponível.
Aumentar a precisão da representação de ponto flutuante geralmente reduz a quantidade de erro de arredondamento acumulado causado por cálculos intermediários. Formatos IEEE menos comuns incluem:
- Precisão quádrupla (binário128). Este é um formato binário que ocupa 128 bits (16 bytes) e seu significadond tem uma precisão de 113 bits (cerca de 34 dígitos decimais).
- Decimal64 e decimal128 formatos de ponto flutuante. Estes formatos, juntamente com o formato decimal32, são destinados a realizar arredondamento decimal corretamente.
- Metade precisão, também chamada de binário16, um valor de ponto flutuante de 16 bits. Está sendo usado na linguagem gráfica NVIDIA Cg, e no padrão openEXR.
Qualquer inteiro com valor absoluto menor que 224 pode ser representado exatamente no formato de precisão simples, e qualquer inteiro com valor absoluto menor que 253 pode ser exatamente representado no formato de dupla precisão. Além disso, uma ampla gama de potências de 2 vezes esse número pode ser representada. Às vezes, essas propriedades são usadas para dados inteiros puros, para obter inteiros de 53 bits em plataformas que possuem flutuadores de precisão dupla, mas apenas inteiros de 32 bits.
O padrão especifica alguns valores especiais e sua representação: infinito positivo (+∞), infinito negativo (−∞), um zero negativo (-0) distinto do zero comum ("positivo") e "não é um número" valores (NaNs).
A comparação de números de ponto flutuante, conforme definido pelo padrão IEEE, é um pouco diferente da comparação usual de números inteiros. Zero negativo e positivo comparam igual, e cada NaN compara desigual a cada valor, incluindo ele mesmo. Todos os números finitos de ponto flutuante são estritamente menores que +∞ e estritamente maiores que −∞ e são ordenados da mesma forma forma como seus valores (no conjunto dos números reais).
Representação interna
Números de ponto flutuante são normalmente agrupados em um dado de computador como o bit de sinal, o campo expoente e o significando ou mantissa, da esquerda para a direita. Para os formatos binários IEEE 754 (básico e estendido) que possuem implementações de hardware existentes, eles são distribuídos da seguinte forma:
Tipo | Sinalização | Exponente | Campo significativo | Total de bits | Viés exonente | Precisão de bits | Número de dígitos decimais | |
---|---|---|---|---|---|---|---|---|
Meia (IEEE 754-2008) | 1 | 5 | 10. | 16. | 15 | 11 | ~3.3 | |
Único | 1 | 8 | 23 | 32 | 127 | 24. | - 7.2 | |
Duplo | 1 | 11 | 52 | 64 | 1023 | 53 | -15.9 | |
precisão estendida x86 | 1 | 15 | 64 | 80 | 16383 | 64 | -19.2 | |
Quadra | 1 | 15 | 112 | 128 | 16383 | 113 | - 34.0 |
Embora o expoente possa ser positivo ou negativo, em formatos binários ele é armazenado como um número sem sinal que possui um "bias" adicionado a ele. Os valores de todos os 0s neste campo são reservados para os zeros e números subnormais; valores de todos os 1s são reservados para infinitos e NaNs. A faixa do expoente para números normais é [−126, 127] para precisão simples, [−1022, 1023] para dupla ou [−16382, 16383] para quad. Os números normais excluem valores subnormais, zeros, infinitos e NaNs.
Nos formatos de intercâmbio binário IEEE, o 1 bit inicial de um significando normalizado não é realmente armazenado no datum do computador. É chamado de "oculto" ou "implícita" pedaço. Por causa disso, o formato de precisão simples na verdade tem um significando com 24 bits de precisão, o formato de precisão dupla tem 53 e o quad tem 113.
Por exemplo, foi mostrado acima que π, arredondado para 24 bits de precisão, tem:
- sinal = 0; e = 1; S = 110010010000111111011011 (incluindo o bit oculto)
A soma do viés do expoente (127) e do expoente (1) é 128, então isso é representado no formato de precisão simples como
- 0 10000000 100100001111011011 (excluindo o bit oculto) = 40490 FDB como um número hexadecimal.
Um exemplo de layout para ponto flutuante de 32 bits é
e o layout de 64 bits ("duplo") é semelhante.
Outros formatos notáveis de ponto flutuante
Além dos formatos padrão IEEE 754 amplamente usados, outros formatos de ponto flutuante são usados, ou foram usados, em certas áreas específicas de domínio.
- O Microsoft Binary Format (MBF) foi desenvolvido para os produtos de linguagem Microsoft BASIC, incluindo o primeiro produto da Microsoft sempre o Altair BASIC (1975), TRS-80 LEVEL II, MBASIC CP/M, BASICA IBM PC 5150, GW-BASIC e QuickBASIC da MS-DOS antes da versão 4.00. QuickBASIC versão 4.00 e 4.50 mudou para o formato IEEE 754-1985, mas pode reverter para o formato MBF usando a opção de comando /MBF. MBF foi projetado e desenvolvido em um simulado Intel 8080 por Monte Davidoff, um dormitório de Bill Gates, durante a primavera de 1975 para o MITS Altair 8800. A versão inicial de julho de 1975 apoiou um formato de única precisão (32 bits) devido ao custo da memória MITS Altair 8800 4-kilobytes. Em dezembro de 1975, a versão 8-kilobytes adicionou um formato de dupla precisão (64 bits). Um formato de variante de uma única precisão (40 bits) foi adotado para outros CPUs, notavelmente o MOS 6502 (Apple //, Commodore PET, Atari), Motorola 6800 (MITS Altair 680) e Motorola 6809 (TRS-80 Color Computer). Todos os produtos de linguagem da Microsoft de 1975 a 1987 usaram o Formato Binário da Microsoft até que a Microsoft adotasse o formato padrão IEEE-754 em todos os seus produtos a partir de 1988 para suas versões atuais. MBF consiste no formato de mono-precisão MBF (32 bits, "6 dígitos BASIC"), o formato de precisão estendida MBF (40 bits, "9 dígitos BASIC"), e o formato de dupla-precisão MBF (64 bits); cada um deles é representado com um exponente de 8 bits, seguido de um bit de sinal, seguido de um significado de respectivamente 23, 31 e 55 bits.
- O formato Bfloat16 requer a mesma quantidade de memória (16 bits) que o formato de meia-precisão IEEE 754, mas aloca 8 bits ao expoente em vez de 5, proporcionando assim a mesma gama que um número de única precisão IEEE 754. O tradeoff é uma precisão reduzida, uma vez que o campo significand é reduzido de 10 a 7 bits. Este formato é usado principalmente no treinamento de modelos de aprendizado de máquina, onde o intervalo é mais valioso do que a precisão. Muitos aceleradores de aprendizado de máquina fornecem suporte de hardware para este formato.
- O formato TensorFloat-32 combina os 8 bits de expoente do Bfloat16 com os 10 bits de campo significativo de meio-precisão, resultando em um tamanho de 19 bits. Este formato foi introduzido pela Nvidia, que fornece suporte de hardware para ele nos Núcleos Tensor de suas GPUs com base na arquitetura Nvidia Ampere. A desvantagem deste formato é o seu tamanho, que não é um poder de 2. No entanto, de acordo com Nvidia, este formato só deve ser usado internamente por hardware para acelerar as computações, enquanto entradas e saídas devem ser armazenados no formato IEEE 754 de única precisão de 32 bits.
- As GPUs de arquitetura Hopper fornecem dois formatos FP8: um com a mesma faixa numérica que meia-precisão (E5M2) e um com maior precisão, mas menos alcance (E4M3).
Tipo | Sinalização | Exponente | Trailing significand field | Total de bits |
---|---|---|---|---|
FP8 (E4M3) | 1 | 4 | 3 | 8 |
FP8 (E5M2) | 1 | 5 | 2 | 8 |
Meia precisão | 1 | 5 | 10. | 16. |
Bfloat16 | 1 | 8 | 7 | 16. |
TensorFloat-32 | 1 | 8 | 10. | 19 |
Precisão única | 1 | 8 | 23 | 32 |
Números representáveis, conversão e arredondamento
Por sua natureza, todos os números expressos no formato de ponto flutuante são números racionais com uma expansão final na base relevante (por exemplo, uma expansão decimal final na base 10 ou uma expansão binária final na base 2). Números irracionais, como π ou √2, ou números racionais não terminados, devem ser aproximados. O número de dígitos (ou bits) de precisão também limita o conjunto de números racionais que podem ser representados com exatidão. Por exemplo, o número decimal 123456789 não pode ser representado exatamente se apenas oito dígitos decimais de precisão estiverem disponíveis (ele seria arredondado para um dos dois valores representáveis transversais, 12345678 × 101 ou 12345679 × 10< sup>1), o mesmo se aplica a dígitos sem terminação (.5 para ser arredondado para 0,55555555 ou 0,55555556).
Quando um número é representado em algum formato (como uma cadeia de caracteres) que não é uma representação nativa de ponto flutuante suportada em uma implementação de computador, será necessária uma conversão antes de poder ser usado nessa implementação. Se o número puder ser representado exatamente no formato de ponto flutuante, a conversão será exata. Se não houver uma representação exata, a conversão requer uma escolha de qual número de ponto flutuante usar para representar o valor original. A representação escolhida terá um valor diferente do original, e o valor assim ajustado é chamado de valor arredondado.
Se um número racional tem ou não uma expansão terminal depende da base. Por exemplo, na base 10 o número 1/2 tem uma expansão final (0,5) enquanto o número 1/3 não (0,333...). Na base 2, apenas racionais com denominadores que são potências de 2 (como 1/2 ou 3/16) são terminantes. Qualquer racional com um denominador que tenha um fator primo diferente de 2 terá uma expansão binária infinita. Isso significa que os números que parecem ser curtos e exatos quando escritos em formato decimal podem precisar ser aproximados quando convertidos em ponto flutuante binário. Por exemplo, o número decimal 0,1 não é representável em ponto flutuante binário de qualquer precisão finita; a representação binária exata teria um "1100" sequência continuando infinitamente:
- e = −4; S = 110011001100110011001100110011001100110011...,
onde, como anteriormente, s é o significando e e é o expoente.
Quando arredondado para 24 bits, torna-se
- e = −4; S = 110011001100110011001101,
que na verdade é 0,100000001490116119384765625 em decimal.
Como outro exemplo, o número real π, representado em binário como uma sequência infinita de bits é
- 11.00100100001111110101000100010000101101000110000100011010011...
mas é
- 11.0010010000111111011
quando aproximado por arredondamento para uma precisão de 24 bits.
No ponto flutuante de precisão simples binário, isso é representado como s = 1.10010010000111111011011 com e = 1. Isso tem um valor decimal de
- 3.1415927410125732421875,
enquanto uma aproximação mais precisa do valor verdadeiro de π é
- 3.141592653579323846264338327950...
O resultado do arredondamento difere do valor real em cerca de 0,03 partes por milhão e corresponde à representação decimal de π nos primeiros 7 dígitos. A diferença é o erro de discretização e é limitada pelo epsilon da máquina.
A diferença aritmética entre dois números de ponto flutuante representáveis consecutivos que têm o mesmo expoente é chamada de unidade na última posição (ULP). Por exemplo, se não houver nenhum número representável entre os números representáveis 1,45a70c22hex e 1,45a70c24hex, o ULP é 2×16−8, ou 2-31. Para números com uma parte expoente de base 2 de 0, ou seja, números com um valor absoluto maior ou igual a 1, mas menor que 2, um ULP é exatamente 2−23 ou cerca de 10− 7 em precisão simples e exatamente 2−53 ou cerca de 10−16 em precisão dupla. O comportamento obrigatório do hardware compatível com IEEE é que o resultado esteja dentro da metade de um ULP.
Modos de arredondamento
O arredondamento é usado quando o resultado exato de uma operação de ponto flutuante (ou uma conversão para o formato de ponto flutuante) precisaria de mais dígitos do que o número de dígitos no significando. IEEE 754 requer arredondamento correto: ou seja, o resultado arredondado é como se aritmética infinitamente precisa fosse usada para calcular o valor e depois arredondada (embora na implementação sejam necessários apenas três bits extras para garantir isso). Existem vários esquemas de arredondamento diferentes (ou modos de arredondamento). Historicamente, o truncamento era a abordagem típica. Desde a introdução do IEEE 754, o método padrão (arredondar para o mais próximo, empates para o par, às vezes chamado de arredondamento do banqueiro) é mais comumente usado. Este método arredonda o resultado ideal (infinitamente preciso) de uma operação aritmética para o valor representável mais próximo e fornece essa representação como resultado. Em caso de empate, escolhe-se o valor que faria o significando terminar em dígito par. O padrão IEEE 754 exige que o mesmo arredondamento seja aplicado a todas as operações algébricas fundamentais, incluindo raiz quadrada e conversões, quando houver um resultado numérico (não NaN). Isso significa que os resultados das operações IEEE 754 são completamente determinados em todos os bits do resultado, exceto pela representação de NaNs. (As funções de "Biblioteca", como cosseno e log, não são obrigatórias.)
Opções alternativas de arredondamento também estão disponíveis. IEEE 754 especifica os seguintes modos de arredondamento:
- redondo para mais próximo, onde os laços voltam ao dígito uniforme mais próximo na posição exigida (o padrão e, de longe, o modo mais comum)
- redondo para mais próximo, onde os laços se afastam de zero (opcional para ponto flutuante binário e comumente usado em decimal)
- arredonda para cima (para cima +∞; resultados negativos ao redor para zero)
- arredonda para baixo (para cima −∞; resultados negativos ao redor de zero)
- round em direção a zero (truncation; é semelhante ao comportamento comum de conversões float-to-integer, que convertem −3.9 a −3 e 3.9 a 3)
Modos alternativos são úteis quando a quantidade de erro introduzida deve ser limitada. Os aplicativos que exigem um erro limitado são ponto flutuante de precisão múltipla e aritmética de intervalo. Os modos alternativos de arredondamento também são úteis no diagnóstico de instabilidade numérica: se os resultados de uma sub-rotina variam substancialmente entre o arredondamento para + e - infinito, então é provável que ela seja numericamente instável e afetada pelo erro de arredondamento.
Conversão de binário para decimal com número mínimo de dígitos
Converter um número de ponto flutuante binário de precisão dupla em uma string decimal é uma operação comum, mas um algoritmo que produz resultados precisos e mínimos não apareceu impresso até 1990, com Steele and White's Dragon4. Algumas das melhorias desde então incluem:
- David M. Gay's Dtoa.c, uma implementação prática de código aberto de muitas ideias no Dragon4.
- Grisu3, com um 4× speedup como ele remove o uso de bignums. Deve ser usado com uma desvantagem, pois falha por ~0,5% dos casos.
- Errol3, um algoritmo sempre bem sucedido semelhante a, mas mais lento do que, Grisu3. Aparentemente, não é tão bom como um Grisu interminável com a desvantagem.
- Ryū, um algoritmo sempre bem sucedido que é mais rápido e simples do que Grisu3.
- Schubfach, um algoritmo sempre bem sucedido que é baseado em uma ideia semelhante a Ryū, desenvolvido quase simultaneamente e independentemente. Executa melhor do que Ryū e Grisu3 em determinados benchmarks.
Muitos tempos de execução de linguagem moderna usam Grisu3 com um fallback Dragon4.
Conversão de decimal para binário
O problema de analisar uma string decimal em uma representação FP binária é complexo, com um analisador preciso não aparecendo até o trabalho de Clinger em 1990 (implementado em dtoa.c). Da mesma forma, o trabalho progrediu na direção de uma análise mais rápida.
Operações de ponto flutuante
Para facilitar a apresentação e compreensão, a raiz decimal com precisão de 7 dígitos será usada nos exemplos, como no formato IEEE 754 decimal32. Os princípios fundamentais são os mesmos em qualquer base ou precisão, exceto que a normalização é opcional (não afeta o valor numérico do resultado). Aqui, s denota o significando e e denota o expoente.
Adição e subtração
Um método simples para adicionar números de ponto flutuante é primeiro representá-los com o mesmo expoente. No exemplo abaixo, o segundo número é deslocado para a direita em três dígitos e, em seguida, procede-se com o método de adição usual:
123456.7 = 1.234567 × 10^5 101.7654 = 1.017654 × 10^2 = 0.001017654 × 10^5
Assim: 123456.7 + 101.7654 = (1.234567 × 10^5) + (1.017654 × 10^2) = (1.234567 × 10^5) + (0.001017654 × 10^5) = (1.234567 + 0.001017654) × 10^5 = 1.235584654 × 10^5
Em detalhes:
e=5; s=1.234567 (123456.7) + e=2; s=1.017654 (101.7654)
e=5; s=1.234567 + e=5; s=0.001017654 (depois de deslocamento) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- e=5; s=1.235584654 (verdadeira soma: 123558.4654)
Este é o resultado verdadeiro, a soma exata dos operandos. Será arredondado para sete dígitos e depois normalizado, se necessário. O resultado final é
e=5; s=1.235585 (valor final: 123558.5)
Os três dígitos mais baixos do segundo operando (654) são essencialmente perdidos. Este é um erro de arredondamento. Em casos extremos, a soma de dois números diferentes de zero pode ser igual a um deles:
e=5; s=1.234567 + e=−3; s=9.876543
e=5; s=1.234567 + e=5; s=0.00000009876543 (após a mudança) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- e=5; s=1.23456709876543 (verdadeira soma) e=5; s=1.234567 (depois de arredondamento e normalização)
Nos exemplos conceituais acima, parece que um grande número de dígitos extras precisaria ser fornecido pelo adicionador para garantir o arredondamento correto; no entanto, para adição ou subtração binária usando técnicas de implementação cuidadosas, apenas um bit guard, um bit arredondamento e um bit adesivo extra precisam ser transportados além a precisão dos operandos.
Outro problema de perda de significância ocorre quando aproximações de dois números quase iguais são subtraídos. No exemplo a seguir e = 5; s = 1.234571 e e = 5; s = 1,234567 são aproximações dos racionais 123457,1467 e 123456,659.
e=5; s=1.234571 - e=5; s=1.234567 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- e=5; s=0,000004 e=−1; s=4.000000 (depois de arredondamento e normalização)
A diferença de ponto flutuante é calculada exatamente porque os números são próximos - o lema Sterbenz garante isso, mesmo no caso de subfluxo quando o subfluxo gradual é suportado. Apesar disso, a diferença dos números originais é e = −1; s = 4,877000, que difere mais de 20% da diferença e = −1; s = 4,000000 das aproximações. Em casos extremos, todos os dígitos significativos de precisão podem ser perdidos. Este cancelamento ilustra o perigo de assumir que todos os dígitos de um resultado computado são significativos. Lidar com as consequências desses erros é um tópico da análise numérica; veja também Problemas de precisão.
Multiplicação e divisão
Para multiplicar, os significandos são multiplicados enquanto os expoentes são somados, e o resultado é arredondado e normalizado.
e=3; s=4.734612 × e=5; s=5.417242 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- e=8; s=25.648538980104 (produto verdadeiro) e=8; s=25.64854 (depois de arredondamento) e=9; s=2.564854 (depois da normalização)
Da mesma forma, a divisão é realizada subtraindo o expoente do divisor do expoente do dividendo e dividindo o significando do dividendo pelo significando do divisor.
Não há problemas de cancelamento ou absorção com multiplicação ou divisão, embora pequenos erros possam se acumular à medida que as operações são executadas em sucessão. Na prática, a maneira como essas operações são realizadas na lógica digital pode ser bastante complexa (consulte o algoritmo de multiplicação de Booth e o algoritmo de divisão). Para um método rápido e simples, consulte o método Horner.
Sintaxe literal
Os literais para números de ponto flutuante dependem dos idiomas. Eles normalmente usam e
ou E
para denotar notação científica. A linguagem de programação C e o padrão IEEE 754 também definem uma sintaxe literal hexadecimal com um expoente de base 2 em vez de 10. Em linguagens como C, quando o expoente decimal é omitido, é necessário um ponto decimal para diferenciá-los dos inteiros. Outras linguagens não possuem um tipo inteiro (como JavaScript) ou permitem a sobrecarga de tipos numéricos (como Haskell). Nesses casos, sequências de dígitos como 123
também podem ser literais de ponto flutuante.
Exemplos de literais de ponto flutuante são:
99.9
-5000.12
6.02e23
-3e-45
0x1.fffffep+127
em C e IEEE 754
Lidando com casos excepcionais
A computação de ponto flutuante em um computador pode apresentar três tipos de problemas:
- Uma operação pode ser matematicamente indefinida, como ∞/∞, ou divisão por zero.
- Uma operação pode ser legal em princípio, mas não apoiada pelo formato específico, por exemplo, calculando a raiz quadrada de −1 ou o seio inverso de 2 (ambos resultam em números complexos).
- Uma operação pode ser legal em princípio, mas o resultado pode ser impossível representar no formato especificado, porque o expoente é demasiado grande ou muito pequeno para codificar no campo expoente. Tal evento é chamado de transbordamento (exponente demasiado grande), subfluxo (exponente demasiado pequeno) ou desnormalização (perda de precisão).
Antes do padrão IEEE, tais condições geralmente causavam o encerramento do programa ou acionavam algum tipo de armadilha que o programador poderia capturar. Como isso funcionava dependia do sistema, o que significa que os programas de ponto flutuante não eram portáveis. (O termo "exceção" conforme usado no IEEE 754 é um termo geral que significa uma condição excepcional, que não é necessariamente um erro, e é um uso diferente daquele normalmente definido em linguagens de programação como C++ ou Java, em que uma "exceção" é um fluxo alternativo de controle, mais próximo do que é chamado de "armadilha" na terminologia IEEE 754.)
Aqui, o método padrão necessário para lidar com exceções de acordo com o IEEE 754 é discutido (a interceptação opcional IEEE 754 e outros modos de "tratamento alternativo de exceção" não são discutidos). Exceções aritméticas são (por padrão) necessárias para serem registradas em "adesivos" bits de sinalizador de status. Que eles são "pegajosos" significa que eles não são redefinidos pela próxima operação (aritmética), mas permanecem definidos até serem redefinidos explicitamente. O uso de "pegajoso" Os sinalizadores permitem que o teste de condições excepcionais seja adiado até depois de uma expressão ou sub-rotina completa de ponto flutuante: sem eles, condições excepcionais que não poderiam ser ignoradas exigiriam testes explícitos imediatamente após cada operação de ponto flutuante. Por padrão, uma operação sempre retorna um resultado de acordo com a especificação sem interromper a computação. Por exemplo, 1/0 retorna +∞, ao mesmo tempo em que define o bit sinalizador de divisão por zero (esse padrão de ∞ é projetado para retornar frequentemente um resultado finito quando usado em operações subseqüentes e, portanto, ser ignorado com segurança).
O padrão IEEE 754 original, no entanto, não recomendou operações para lidar com esses conjuntos de bits de flag de exceção aritmética. Portanto, embora tenham sido implementados em hardware, inicialmente as implementações de linguagem de programação normalmente não forneciam um meio de acessá-los (além do montador). Com o tempo, alguns padrões de linguagem de programação (por exemplo, C99/C11 e Fortran) foram atualizados para especificar métodos para acessar e alterar bits de sinalizador de status. A versão 2008 do padrão IEEE 754 agora especifica algumas operações para acessar e manipular os bits de sinalizador aritmético. O modelo de programação é baseado em um único thread de execução e o uso deles por vários threads deve ser tratado por meios fora do padrão (por exemplo, C11 especifica que os sinalizadores têm armazenamento local de thread).
O IEEE 754 especifica cinco exceções aritméticas que devem ser registradas nos sinalizadores de status ("sticky bits"):
- inexacto, definir se o valor arredondado (e retornado) é diferente do resultado matematicamente exato da operação.
- fluxo, definir se o valor arredondado é minúsculo (como especificado no IEEE 754) e inexact (ou talvez limitado a se tem perda de denormalização, de acordo com a versão de 1985 do IEEE 754), retornando um valor subnormal incluindo os zeros.
- excesso de fluxo, definir se o valor absoluto do valor arredondado é demasiado grande para ser representado. Um valor finito infinito ou máximo é retornado, dependendo de qual arredondamento é usado.
- dividir por zero, definir se o resultado é infinito dado operandos finitos, retornando um infinito, ou +∞ ou −∞.
- inválido, definir se um resultado real-valorizado não pode ser retornado, por exemplo, sqrt(−1) ou 0/0, retornando um NaN silencioso.
O valor de retorno padrão para cada uma das exceções é projetado para dar o resultado correto na maioria dos casos, de modo que as exceções podem ser ignoradas na maioria dos códigos. inexacto retorna um resultado arredondado corretamente, e fluxo retorna um valor menor ou igual ao menor número normal positivo em magnitude e quase sempre pode ser ignorado. dividir por zero retorna exatamente o infinito, que normalmente dividirá um número finito e assim dar zero, ou então dará um inválido exceção posteriormente, se não, e assim também pode tipicamente ser ignorado. Por exemplo, a resistência efetiva de n resistores em paralelo (ver figo. 1) é dada por . Se um curto-circuito se desenvolve com set to 0, retornará + infinito que dará uma final de 0, como esperado (veja o exemplo de fração continuado do projeto IEEE 754 racionale para outro exemplo).
ExceçõesEstouro e inválido geralmente não podem ser ignoradas, mas não representam necessariamente erros: por exemplo, uma rotina de localização de raiz, como parte de sua operação normal, pode avaliar uma função passada em valores fora de seu domínio, retornando NaN e um sinalizador de exceção inválido a ser ignorado até encontrar um ponto inicial útil.
Problemas de precisão
O fato de que os números de ponto flutuante não podem representar com precisão todos os números reais, e que as operações de ponto flutuante não podem representar com precisão as operações aritméticas verdadeiras, leva a muitas situações surpreendentes. Isso está relacionado à precisão finita com que os computadores geralmente representam números.
Por exemplo, a não representabilidade de 0,1 e 0,01 (em binário) significa que o resultado da tentativa de quadrar 0,1 não é 0,01 nem o número representável mais próximo dele. Na representação de 24 bits (precisão única), 0,1 (decimal) foi fornecido anteriormente como e = −4; s = 110011001100110011001101, que é
A elevação ao quadrado desse número dá
Equadrá-lo com hardware de ponto flutuante de precisão única (com arredondamento) fornece
Mas o número representável mais próximo de 0,01 é
Além disso, a não representabilidade de π (e π/2) significa que uma tentativa de cálculo de tan(π/2) não produzirá um resultado infinito, nem mesmo transbordará nos formatos usuais de ponto flutuante (assumindo uma implementação precisa de tan). Simplesmente não é possível para o hardware padrão de ponto flutuante tentar calcular tan(π/2), porque π/2 não pode ser representado exatamente. Este cálculo em C:
/* O suficiente para ter certeza de que temos a aproximação correta. *duplo Pai! = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 3.141592658979384626433832795;duplo zangão. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = bronzeado(Pai!/2.0);
dará um resultado de 16331239353195370.0. Em precisão simples (usando a função tanf
), o resultado será −22877332.0.
Da mesma forma, uma tentativa de cálculo de sin(π) não resultará em zero. O resultado será (aproximadamente) 0.1225×10−15 em precisão dupla, ou −0.8742×10−7 em precisão única.
Enquanto adição e multiplicação de ponto flutuante são comutativas (a + b = b + a e a × b = b × a), eles não são necessariamente associativos. Ou seja, (a + b) + c não é necessariamente igual a a + (b + c). Usando aritmética decimal de significando de 7 dígitos:
a = 1234.567, b = 45.67834, c = 0,0004
(a + b) + c: 1234.567 (a) + 45.67834 (b) O que se passa? 1280.24534 rodadas para 1280.245
1280.245 (a + b) + 0,0004 (c) O que se passa? 1280.2454 rodadas para 1280.245 ← (a + b) + c
a + (b + c): 45.67834 (b) + 0,0004 (c) O que se passa? 45.67874
1234.567 (a) + 45.67874 (b + c) O que se passa? 1280.24574 rodadas para 1280.246 ← a + (b + c)
Eles também não são necessariamente distributivos. Ou seja, (a + b) × c pode não ser o mesmo que a × c + b × c:
1234.567 × 3.333333 = 4115.223 1.234567 × 3.333333 = 4.115223 4115.223 + 4.115223 = 4119.338 mas... 1234.567 + 1.234567 = 1235.802 1235.802 × 3.333333 = 4119.340
Além da perda de significância, incapacidade de representar números como π e 0,1 com exatidão e outras pequenas imprecisões, podem ocorrer os seguintes fenômenos:
- Cancelamento: subtração de operandos quase iguais pode causar perda extrema de precisão. Quando subtraímos dois números quase iguais, definimos os dígitos mais significativos para zero, deixando-nos com apenas os dígitos insignificantes e mais errôneos. Por exemplo, ao determinar um derivado de uma função, a seguinte fórmula é usada:
Intuitivamente um quereria um h muito perto de zero; no entanto, ao usar operações de ponto flutuante, o menor número não dará a melhor aproximação de um derivado. Como h cresce menor, a diferença entre f(um + h) e f(um) cresce menor, cancelando os dígitos mais significativos e menos errôneos e tornando os dígitos mais errôneos mais importantes. Como resultado, o menor número de h possível dará uma aproximação mais errônea de um derivado do que um número um tanto maior. Este é talvez o problema mais comum e sério de precisão.
- Conversões para inteiros não são intuitivas: conversão (63.0/9.0) para integer produz 7, mas converter (0.63/0.09) pode render 6. Isso é porque as conversões geralmente truncam em vez de rodada. As funções de assoalho e teto podem produzir respostas que estão fora por um do valor intuitivamente esperado.
- Faixa exponente limitada: os resultados podem transbordar o rendimento infinito, ou subfluxo, produzindo um número subnormal ou zero. Nestes casos a precisão será perdida.
- Teste para divisão segura é problemático: Verificar que o divisor não é zero não garante que uma divisão não transborda.
- Testar para a igualdade é problemático. Duas sequências computacionais que são matematicamente iguais podem bem produzir diferentes valores de ponto flutuante.
Incidentes
- Em 25 de fevereiro de 1991, uma perda de significância em uma bateria de mísseis MIM-104 Patriot impediu que ele interceptasse um míssil Scud em Dhahran, Arábia Saudita, contribuindo para a morte de 28 soldados do 14o Desequimento Quartermaster do Exército dos EUA.
Precisão da máquina e análise de erros retrógrados
Precisão da máquina é uma quantidade que caracteriza a precisão de um sistema de ponto flutuante e é usada na análise de erro inversa de algoritmos de ponto flutuante. Também é conhecido como arredondamento de unidade ou máquina epsilon. Geralmente denotado Εmach, seu valor depende do arredondamento específico que está sendo usado.
Com arredondamento para zero,
Isso é importante, pois limita o erro relativo na representação de qualquer número real diferente de zero x< /var> dentro do intervalo normalizado de um sistema de ponto flutuante:
A análise de erro retrógrada, cuja teoria foi desenvolvida e popularizada por James H. Wilkinson, pode ser usada para estabelecer que um algoritmo implementando uma função numérica é numericamente estável. A abordagem básica é mostrar que, embora o resultado calculado, devido a erros de arredondamento, não seja exatamente correto, é a solução exata para um problema próximo com dados de entrada ligeiramente perturbados. Se a perturbação necessária for pequena, da ordem da incerteza nos dados de entrada, então os resultados são, em certo sentido, tão precisos quanto os dados "merecem". O algoritmo é então definido como estável para trás. A estabilidade é uma medida da sensibilidade a erros de arredondamento de um determinado procedimento numérico; por outro lado, o número de condição de uma função para um determinado problema indica a sensibilidade inerente da função a pequenas perturbações em sua entrada e é independente da implementação usada para resolver o problema.
Como um exemplo trivial, considere uma expressão simples dando o produto interno de (comprimento dois) vetores e , então
onde
onde
por definição, que é a soma de dois dados de entrada levemente perturbados (na ordem de Εmach), e portanto é estável para trás. Para exemplos mais realistas em álgebra linear numérica, consulte Higham 2002 e outras referências abaixo.
Minimizando o efeito de problemas de precisão
Embora as operações aritméticas individuais do IEEE 754 tenham precisão garantida de meio ULP, fórmulas mais complicadas podem sofrer erros maiores por vários motivos. A perda de precisão pode ser substancial se um problema ou seus dados estiverem mal condicionados, o que significa que o resultado correto é hipersensível a pequenas perturbações em seus dados. No entanto, mesmo funções bem condicionadas podem sofrer grande perda de precisão se for usado um algoritmo numericamente instável para esses dados: formulações aparentemente equivalentes de expressões em uma linguagem de programação podem diferir acentuadamente em sua estabilidade numérica. Uma abordagem para remover o risco de tal perda de precisão é o projeto e análise de algoritmos numericamente estáveis, que é um objetivo do ramo da matemática conhecido como análise numérica. Outra abordagem que pode proteger contra o risco de instabilidades numéricas é a computação de valores intermediários (scratch) em um algoritmo com uma precisão maior do que o resultado final requer, o que pode remover ou reduzir em ordens de grandeza esse risco: IEEE 754 quádruplo precisão e precisão estendida são projetadas para esta finalidade ao computar em precisão dupla.
Por exemplo, o algoritmo a seguir é uma implementação direta para calcular a função A(x) = (x−1) / (exp(x−1) − 1) que é bem condicionado em 1,0, porém pode se mostrar numericamente instável e perder até metade dos dígitos significativos carregados pela aritmética quando calculado próximo 1.0.
duplo A(duplo X)( duplo Y, Z.; // [1] Y = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = X - Não. 1.0.; Z. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = exp(Y); se (Z. ! 1.0.) Z. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Y / (Z. - Não. 1.0.); // [2] retorno Z.;?
Se, no entanto, os cálculos intermediários forem todos executados em precisão estendida (por exemplo, definindo a linha [1] como C99 long double
), então a precisão total no resultado duplo final pode ser mantida. Alternativamente, uma análise numérica do algoritmo revela que, se a seguinte alteração não óbvia na linha [2] for feita:
Z. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = log(Z.) / (Z. - Não. 1.0.);
então o algoritmo se torna numericamente estável e pode calcular com precisão dupla total.
Para manter as propriedades de tais programas numericamente estáveis cuidadosamente construídos, é necessário um manuseio cuidadoso por parte do compilador. Certas "otimizações" que os compiladores podem fazer (por exemplo, reordenar operações) pode funcionar contra os objetivos do software bem comportado. Há alguma controvérsia sobre as falhas de compiladores e projetos de linguagem nessa área: C99 é um exemplo de linguagem em que tais otimizações são cuidadosamente especificadas para manter a precisão numérica. Consulte as referências externas no final deste artigo.
Um tratamento detalhado das técnicas para escrever software de ponto flutuante de alta qualidade está além do escopo deste artigo, e o leitor é referido, e as outras referências na parte inferior deste artigo. Kahan sugere várias regras práticas que podem diminuir substancialmente em ordens de magnitude o risco de anomalias numéricas, além de, ou em vez de, uma análise numérica mais cuidadosa. Isso inclui: como observado acima, computar todas as expressões e resultados intermediários na mais alta precisão suportada no hardware (uma regra prática comum é carregar o dobro da precisão do resultado desejado, ou seja, calcular em precisão dupla para um resultado final de precisão simples, ou em precisão dupla estendida ou quádrupla para resultados de precisão dupla); e arredondar os dados de entrada e os resultados apenas para a precisão necessária e suportada pelos dados de entrada (carregar precisão excessiva no resultado final além da exigida e suportada pelos dados de entrada pode ser enganoso, aumenta o custo de armazenamento e diminui a velocidade, e os bits em excesso podem afetam a convergência de procedimentos numéricos: notavelmente, a primeira forma do exemplo iterativo dado abaixo converge corretamente ao usar esta regra de ouro). Seguem breves descrições de vários problemas e técnicas adicionais.
Como as frações decimais muitas vezes não podem ser exatamente representadas em ponto flutuante binário, essa aritmética é melhor quando está sendo usada simplesmente para medir quantidades do mundo real em uma ampla gama de escalas (como o período orbital de um lua em torno de Saturno ou a massa de um próton) e, na pior das hipóteses, quando se espera modelar as interações de quantidades expressas como sequências decimais que se espera que sejam exatas. Um exemplo do último caso são os cálculos financeiros. Por esse motivo, o software financeiro tende a não usar uma representação de número de ponto flutuante binário. O "decimal" tipo de dados das linguagens de programação C# e Python e os formatos decimais do padrão IEEE 754-2008, são projetados para evitar os problemas de representações binárias de ponto flutuante quando aplicadas a valores decimais exatos inseridos por humanos e fazer com que a aritmética sempre se comporte como esperado quando os números são impressos em decimal.
As expectativas da matemática podem não ser realizadas no campo da computação de ponto flutuante. Por exemplo, sabe-se que e isso , no entanto, estes fatos não podem ser invocados quando as quantidades envolvidas são o resultado da computação de ponto flutuante.
O uso do teste de igualdade (if (x==y)...
) requer cuidado ao lidar com números de ponto flutuante. Mesmo expressões simples como 0.6/0.2-3==0
, na maioria dos computadores, não serão verdadeiras (na precisão dupla IEEE 754, por exemplo, 0.6/0.2 - 3
é aproximadamente igual a -4,44089209850063e-16). Consequentemente, esses testes às vezes são substituídos por testes "difusos" comparações (if (abs(x-y) < epsilon)...
, onde epsilon é suficientemente pequeno e adaptado ao aplicativo, como 1.0E-13). A sabedoria de fazer isso varia muito e pode exigir análise numérica até o limite do epsilon. Os valores derivados da representação dos dados primários e suas comparações devem ser executados com uma precisão mais ampla e estendida para minimizar o risco de tais inconsistências devido a erros de arredondamento. Geralmente é melhor organizar o código de forma que tais testes sejam desnecessários. Por exemplo, em geometria computacional, testes exatos de se um ponto está fora ou em uma linha ou plano definido por outros pontos podem ser realizados usando precisão adaptativa ou métodos aritméticos exatos.
Pequenos erros na aritmética de ponto flutuante podem aumentar quando os algoritmos matemáticos executam operações um número enorme de vezes. Alguns exemplos são inversão de matrizes, computação de autovetores e resolução de equações diferenciais. Esses algoritmos devem ser projetados com muito cuidado, usando abordagens numéricas, como refinamento iterativo, para que funcionem bem.
A soma de um vetor de valores de ponto flutuante é um algoritmo básico em computação científica e, portanto, é essencial saber quando pode ocorrer perda de significância. Por exemplo, se alguém está adicionando um número muito grande de números, os acréscimos individuais são muito pequenos em comparação com a soma. Isso pode levar à perda de significância. Uma adição típica seria algo como
3253.671 + 3.141276 ----------------- 325,812
Os 3 dígitos baixos dos adendos são efetivamente perdidos. Suponha, por exemplo, que seja necessário somar muitos números, todos aproximadamente iguais a 3. Depois de 1.000 deles terem sido adicionados, a soma aproximada é de cerca de 3.000; os dígitos perdidos não são recuperados. O algoritmo de soma Kahan pode ser usado para reduzir os erros.
O erro de arredondamento pode afetar a convergência e a precisão de procedimentos numéricos iterativos. Como exemplo, Arquimedes aproximou π calculando os perímetros de polígonos que inscrevem e circunscrevem um círculo, começando com hexágonos e dobrando sucessivamente o número de lados. Conforme observado acima, os cálculos podem ser reorganizados de maneira matematicamente equivalente, mas menos propenso a erros (análise numérica). Duas formas da fórmula de recorrência para o polígono circunscrito são:
- Primeira forma:
- segunda forma:
- , convergindo como
Aqui está um cálculo usando IEEE "double" (um significando com 53 bits de precisão) aritmética:
6 × 2Eu... ×Eu..., primeira forma 6 × 2Eu... ×Eu..., segundo formulário ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 0 3.46410161377543863 3.46410161377543863 1 32153903091734710173 3.2153903091734723496 2 3.1596599420974940120 3.1596599420975006733 3 3.146086151314012979 3.146086151314352708 4 3.14271459963136334 3.14271459963689225 5 3.1418730499801259536 3.1418730499798241950 6 3.14166274705480841 3.1416627470568494473 7 3.141610176598005 3.1416101766046906629 8 3.14159703432307762 3.1415970343215275928 9 3.1415937488171150615 3.14159NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 NT1 10. 3.1415929278733740748 3.141592927385097988 11 3.14159272562285041 3.141592722086148377 12 3.1415926717412858693 3.1415926707019992125 13 3.1415926189011456060 3.1415926578678454728 14 3.1415926717412858693 3.1415926546593073709 15 3.1415919358822321783 3.1415926538571730119 16. 3.1415926717412858693 3.1415926536566394222 17. 3.1415810075796233302 3.1415926536065061913 18. 3.1415926717412858693 3.1415926535939728836 19 3.1414061547378810956 3.1415926535908393901 20. 3.1405434924008406305 3.1415926535900560168 21 3.1400068646912273617 3.14159265358986083 22 3.13494537585929919 3.1415926535898122118 23 3.1400068646912273617 3.1415926535897995552 24. 322451524353455443 3.14159265358979689 25 3.1415926535897962246 26 3.1415926535897962246 27 3.1415926535897962246 28 3.1415926535897962246 O verdadeiro valor é 3.14159265358979323846264338327...
Enquanto as duas formas da fórmula de recorrência são claramente matematicamente equivalentes, a primeira subtraia 1 de um número extremamente próximo de 1, levando a uma perda cada vez mais problemática de dígitos significativos. À medida que a recorrência é aplicada repetidamente, a precisão melhora primeiro, mas depois se deteriora. Nunca fica melhor que cerca de 8 dígitos, embora a aritmética de 53 bits deva ser capaz de cerca de 16 dígitos de precisão. Quando a segunda forma da recorrência é usada, o valor converge para 15 dígitos de precisão.
" Matemática rápida " Otimização
A falta de associatividade acima mencionada das operações de ponto flutuante em geral significa que os compiladores não podem reordenar efetivamente expressões aritméticas que puderam com aritmética inteira e de ponto fixo, apresentando um obstáculo nas otimizações, como eliminação e auto-vetorização da subexpressão comum. A matemática rápida " A opção em muitos compiladores (ICC, GCC, CLANG, MSVC...) liga a reafirmação junto com suposições inseguras, como falta de NAN e números infinitos no IEEE 754. Alguns compiladores também oferecem opções mais granulares para ativar apenas a reassociação. Em ambos os casos, o programador é exposto a muitas das armadilhas de precisão mencionadas acima para a parte do programa usando " Fast " matemática.
Em alguns compiladores (GCC e CLANG), ativando " Fast " A matemática pode fazer com que o programa desative os carros alegóricos subnormais na startup, afetando o comportamento de ponto flutuante não apenas do código gerado, mas também de qualquer programa usando esse código como uma biblioteca.
Na maioria dos compiladores fortran, conforme permitido pela ISO/IEC 1539-1: 2004 Fortran Standard, a reafirmação é o padrão, com quebra amplamente evitada pelo " Protect Parens " configuração (também por padrão). Essa configuração impede que o compilador reafirme além dos limites dos parênteses. O Intel Fortran Compiler é um outlier notável.
Um problema comum em " Fast " A matemática é que as subexpressões não podem ser otimizadas de forma idêntica de um lugar para outro, levando a diferenças inesperadas. Uma interpretação da questão é que " Fast " Atualmente, a matemática implementada possui uma semântica mal definida. Uma tentativa de formalizar " Fast " O otimizações de matemática é visto em icing , um compilador verificado.
Contenido relacionado
Pardal AIM-7
Fechamento algébrico
Alcino