Pelo menos em Java, se eu escrever este código:
float a = 1000.0F;
float b = 0.00004F;
float c = a + b + b;
float d = b + b + a;
boolean e = c == d;
o valor de seria . Acredito que isso seja causado pelo fato de os flutuadores serem muito limitados na maneira de representar números com precisão. Mas não entendo por que apenas mudar a posição de poderia causar essa desigualdade.
Reduzi os s para um nas linhas 3 e 4, como abaixo, mas o valor de se torna :
float a = 1000.0F;
float b = 0.00004F;
float c = a + b;
float d = b + a;
boolean e = c == d;
O que exatamente aconteceu nas linhas 3 e 4? Por que operações de adição com flutuadores não são associativas?
Desde já, obrigado.
arithmetic
floating-point
numerical-algorithms
Zeta conhecido
fonte
fonte
X
um número muito grande eY
um número muito pequeno, como esseX + Y = X
. Aqui,X + Y + -X
será zero. MasX + -X + Y
seráY
.Respostas:
Nas implementações típicas de ponto flutuante, o resultado de uma única operação é produzido como se a operação fosse executada com precisão infinita e arredondada para o número de ponto flutuante mais próximo.
Compare e b + a : O resultado de cada operação executada com precisão infinita é o mesmo; portanto, esses resultados idênticos de precisão infinita são arredondados de maneira idêntica. Em outras palavras, a adição de ponto flutuante é comutativa.a + b b + a
Considere : b é um número de ponto flutuante. Com números binários de ponto flutuante, 2 b também é um número de ponto flutuante (o expoente é maior em um), então b + b é adicionado sem nenhum erro de arredondamento. Então a é adicionado ao valor exato b + b . O resultado é o valor exato 2 b + a , arredondado para o número de ponto flutuante mais próximo.b + b + a b 2 b b + b uma b + b 2 b + a
Tome : a + b é adicionado e haverá um erro de arredondamento r , então obtemos o resultado a + b + r . Adicione b , e o resultado será o valor exato 2 b + a + r , arredondado para o número de ponto flutuante mais próximo.a + b + b a + b r a + b + r b 2 b + a + r
Então, em um caso, , arredondado. No outro caso, 2 b + a + r , arredondado.2 b + a 2 b + a + r
PS. Quer se trate de dois números particulares e b ambos os cálculos se obter o mesmo resultado ou não depende dos números, e sobre o erro de arredondamento no cálculo um + b , e é geralmente difícil de prever. O uso de precisão simples ou dupla não faz diferença para o problema em princípio, mas como os erros de arredondamento são diferentes, haverá valores de aeb em que na precisão única os resultados são iguais e na precisão dupla não são, ou vice-versa. A precisão será muito maior, mas o problema de que duas expressões são matematicamente iguais, mas não iguais na aritmética de ponto flutuante permanece o mesmo.uma b a + b
PPS. Em alguns idiomas, a aritmética de ponto flutuante pode ser executada com maior precisão ou com um intervalo maior de números do que o indicado pelas declarações reais. Nesse caso, seria muito mais provável (mas ainda não garantido) que ambas as somas apresentassem o mesmo resultado.
PPPS. Um comentário perguntou se deveríamos perguntar se os números de ponto flutuante são iguais ou não. Absolutamente se você souber o que está fazendo. Por exemplo, se você classifica uma matriz ou implementa um conjunto, você se mete em um problema terrível se quiser usar alguma noção de "aproximadamente igual". Em uma interface gráfica com o usuário, pode ser necessário recalcular o tamanho do objeto se o tamanho de um objeto foi alterado - você compara oldSize == newSize para evitar esse recálculo, sabendo que, na prática, você quase nunca tem tamanhos quase idênticos, e seu programa está correto mesmo se houver um recálculo desnecessário.
fonte
b
nesta resposta não é 0,00004, é o que você obtém após a conversão e o arredondamento.O formato binário de ponto flutuante suportado por computadores é essencialmente semelhante à notação científica decimal usada por seres humanos.
Um número de ponto flutuante consiste em um sinal, mantissa (largura fixa) e expoente (largura fixa), assim:
A notação científica regular tem um formato semelhante:
Se fizermos aritmética em notação científica com precisão finita, arredondando após cada operação, obtemos todos os mesmos efeitos negativos que o ponto flutuante binário.
Exemplo
Para ilustrar, suponha que usamos exatamente três dígitos após o ponto decimal.
(a + b) + b
Agora calculamos:
Na próxima etapa, é claro:
Portanto (a + b) + b = 9.999 × 10 4 .
(b + b) + a
Mas se fizermos as operações em uma ordem diferente:
Em seguida, calculamos:
Portanto (b + b) + a = 1.000 × 10 5 , que é diferente da nossa outra resposta.
fonte
Java usa a representação de ponto flutuante binário IEEE 754, que dedica 23 dígitos binários à mantissa, que é normalizada para começar com o primeiro dígito significativo (omitido, para economizar espaço).
As partes em vermelho são as mantissas, como na verdade são representadas (antes do arredondamento).
fonte
Recentemente, enfrentamos um problema de arredondamento semelhante. As respostas acima mencionadas estão corretas, porém bastante técnicas.
Eu achei o seguinte uma boa explicação do porquê existem erros de arredondamento. http://csharpindepth.com/Articles/General/FloatingPoint.aspx
TLDR: pontos flutuantes binários não podem ser mapeados com precisão para pontos flutuantes decimais. Isso causa imprecisões que podem se agravar durante operações matemáticas.
Um exemplo usando números flutuantes decimais: 1/3 + 1/3 + 1/3 normalmente seria igual a 1. No entanto, em decimais: 0,333333 + 0,333333 + 0,333333 nunca é exatamente igual a 1,000000
O mesmo acontece ao executar operações matemáticas em decimais binários.
fonte