Hoje, eu estava procurando por um código C ++ (escrito por outra pessoa) e encontrei esta seção:
double someValue = ...
if (someValue < std::numeric_limits<double>::epsilon() &&
someValue > -std::numeric_limits<double>::epsilon()) {
someValue = 0.0;
}
Estou tentando descobrir se isso faz sentido.
A documentação para epsilon()
diz:
A função retorna a diferença entre 1 e o menor valor maior que 1 que é representável [por um duplo].
Isso também se aplica a 0, ou epsilon()
seja, o menor valor é maior que 0? Ou existem números entre 0
e 0 + epsilon
que podem ser representados por um double
?
Caso contrário, a comparação não é equivalente a someValue == 0.0
?
numeric_limits<>::epsilon
é enganoso e irrelevante. O que queremos é assumir 0 se o valor real diferir não mais do que alguns ε de 0. E ε deve ser escolhido com base na especificação do problema, não em um valor dependente da máquina. Eu suspeitaria que o epsilon atual é inútil, já que apenas algumas operações de FP podem acumular um erro maior que isso.Respostas:
Supondo que o IEEE de 64 bits seja duplo, há uma mantissa de 52 bits e um expoente de 11 bits. Vamos dividir em pedaços:
O menor número representável maior que 1:
Portanto:
Existem números entre 0 e epsilon? Muito ... Por exemplo, o número mínimo representável positivo (normal) é:
De fato, existem
(1022 - 52 + 1)×2^52 = 4372995238176751616
números entre 0 e epsilon, que representa 47% de todos os números representáveis positivos ...fonte
0 <= e < 2048
, a mantissa será multiplicada por 2 à potência dee - 1023
. Por exemplo, expoente de2^0
é codificado comoe=1023
,2^1
comoe=1024
e2^-1022
comoe=1
. O valor dee=0
é reservado para subnormais e zero real.2^-1022
é o menor número normal . O menor número é realmente0.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^-1022 = 2^-1074
. Isso é subnormal, o que significa que a parte da mantissa é menor que 1 e, portanto, é codificada com o expoentee=0
.O teste certamente não é o mesmo que
someValue == 0
. A idéia dos números de ponto flutuante é que eles armazenam um expoente e um significando. Eles, portanto, representam um valor com um certo número de números binários significativos de precisão (53 no caso de um duplo IEEE). Os valores representáveis são muito mais densamente compactados perto de 0 do que perto de 1.Para usar um sistema decimal mais familiar, suponha que você armazene um valor decimal "até 4 algarismos significativos" com expoente. Em seguida, o próximo valor representável maior que
1
é1.001 * 10^0
eepsilon
é1.000 * 10^-3
. Mas1.000 * 10^-4
também é representável, assumindo que o expoente possa armazenar -4. Você pode acreditar que um duplo IEEE pode armazenar expoentes menos que o expoente deepsilon
.Você não pode dizer apenas a partir deste código se faz sentido ou não usar
epsilon
especificamente como limite, é necessário examinar o contexto. Pode ser queepsilon
seja uma estimativa razoável do erro no cálculo produzidosomeValue
, e pode ser que não seja.fonte
someValue == 0.0
ou não.Existem números que existem entre 0 e epsilon porque epsilon é a diferença entre 1 e o próximo número mais alto que pode ser representado acima de 1 e não a diferença entre 0 e o próximo número mais alto que pode ser representado acima de 0 (se fosse, esse código faria muito pouco): -
Usando um depurador, pare o programa no final do main e observe os resultados e você verá que o epsilon / 2 é distinto do epsilon, zero e um.
Portanto, essa função pega valores entre +/- epsilon e os torna zero.
fonte
Uma aproximação de epsilon (a menor diferença possível) em torno de um número (1,0, 0,0, ...) pode ser impressa com o seguinte programa. Ele imprime a seguinte saída:
epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
Um pouco de reflexão deixa claro que o epsilon fica menor, quanto menor o número que usamos para analisar seu valor epsilon, porque o expoente pode se ajustar ao tamanho desse número.
fonte
Suponha que estamos trabalhando com números de ponto flutuante de brinquedo que se encaixam em um registro de 16 bits. Há um bit de sinal, um expoente de 5 bits e uma mantissa de 10 bits.
O valor desse número de ponto flutuante é a mantissa, interpretada como um valor decimal binário, multiplicado por dois à potência do expoente.
Em torno de 1, o expoente é igual a zero. Portanto, o menor dígito da mantissa é uma parte em 1024.
Quase 1/2 do expoente é menos um, então a menor parte da mantissa é metade do tamanho. Com um expoente de cinco bits, pode chegar a 16 negativos, quando a menor parte da mantissa vale uma parte em 32m. E no expoente 16 negativo, o valor está em torno de uma parte em 32k, muito mais próximo de zero do que o epsilon em torno de um calculado acima!
Agora, este é um modelo de ponto flutuante de brinquedo que não reflete todas as peculiaridades de um sistema real de ponto flutuante, mas a capacidade de refletir valores menores que epsilon é razoavelmente semelhante aos valores reais de ponto flutuante.
fonte
A diferença entre
X
e o próximo valor deX
varia de acordo comX
.epsilon()
é apenas a diferença entre1
e o próximo valor de1
.A diferença entre
0
e o próximo valor de0
não éepsilon()
.Em vez disso, você pode usar
std::nextafter
para comparar um valor duplo com0
o seguinte:fonte
Eu acho que isso depende da precisão do seu computador. Dê uma olhada nesta tabela : você pode ver que, se o seu epsilon for representado pelo dobro, mas sua precisão for maior, a comparação não será equivalente a
Boa pergunta de qualquer maneira!
fonte
Você não pode aplicar isso a 0, devido a partes de mantissa e expoente. Devido ao expoente, você pode armazenar números muito pequenos, menores que o epsilon, mas quando você tenta fazer algo como (1.0 - "número muito pequeno"), você obtém 1.0. Epsilon é um indicador não de valor, mas de precisão de valor, que está em mantissa. Ele mostra quantos dígitos decimais consequentes corretos podemos armazenar.
fonte
Com o ponto flutuante IEEE, entre o menor valor positivo diferente de zero e o menor valor negativo diferente de zero, existem dois valores: zero positivo e zero negativo. Testar se um valor está entre os menores valores diferentes de zero é equivalente a testar a igualdade com zero; a atribuição, no entanto, pode ter um efeito, pois alteraria um zero negativo para um positivo.
Seria concebível que um formato de ponto flutuante pudesse ter três valores entre os menores valores positivos e negativos finitos: infinitesimal positivo, zero não assinado e infinitesimal negativo. Não conheço nenhum formato de ponto flutuante que funcione dessa maneira, mas esse comportamento seria perfeitamente razoável e sem dúvida melhor do que o do IEEE (talvez não seja o suficiente para valer a pena adicionar hardware extra para suportá-lo, mas matematicamente 1 / (1 / INF), 1 / (- 1 / INF) e 1 / (1-1) devem representar três casos distintos que ilustram três zeros diferentes). Não sei se algum padrão C determinaria que infinitesimais assinados, se existirem, precisariam comparar igual a zero. Se não o fizerem, código como o acima poderia garantir que, por exemplo,
fonte
Então, digamos que o sistema não possa distinguir 1.000000000000000000000 e 1.0000000000000000000000001. isso é 1.0 e 1.0 + 1e-20. Você acha que ainda existem alguns valores que podem ser representados entre -1e-20 e + 1e-20?
fonte
epsilon
. Porque é ponto flutuante , não ponto fixo.Além disso, uma boa razão para ter essa função é remover "denormals" (números muito pequenos que não podem mais usar o "1" inicial implícito e têm uma representação FP especial). Por que você quer fazer isso? Porque algumas máquinas (em particular, algumas Pentium 4s mais antigas) ficam muito, muito lentas ao processar denormals. Outros ficam um pouco mais lentos. Se seu aplicativo realmente não precisar desses números muito pequenos, liberá-los para zero é uma boa solução. Bons locais para considerar isso são os últimos passos de quaisquer filtros IIR ou funções de decaimento.
Veja também: Por que alterar 0,1f para 0 diminui o desempenho em 10x?
e http://en.wikipedia.org/wiki/Denormal_number
fonte