De acordo com esta página java.sun, ==
é o operador de comparação de igualdade para números de ponto flutuante em Java.
No entanto, quando eu digito este código:
if(sectionID == currentSectionID)
no meu editor e executar a análise estática, recebo: "JAVA0078 Valores de ponto flutuante comparados com =="
O que há de errado em usar ==
para comparar valores de ponto flutuante? Qual é a maneira correta de fazer isso?
java
equality
floating-accuracy
user128807
fonte
fonte
Respostas:
a maneira correta de testar carros alegóricos para 'igualdade' é:
onde epsilon é um número muito pequeno como 0,00000001, dependendo da precisão desejada.
fonte
if(Math.abs(sectionID - currentSectionID) < epsilon*sectionID
para resolver esse problema?Math.ulp()
minha resposta a esta pergunta.Os valores de ponto flutuante podem ser desativados um pouco, portanto, podem não ser exatamente iguais. Por exemplo, definindo um float como "6.1" e imprimindo-o novamente, você pode obter um valor relatado de algo como "6.099999904632568359375". Isso é fundamental para a maneira como os carros alegóricos funcionam; portanto, você não deseja compará-los usando a igualdade, mas sim a comparação dentro de um intervalo, ou seja, se o diff do float com o número com o qual você deseja compará-lo for menor que um determinado valor absoluto.
Este artigo no Registro fornece uma boa visão geral de por que esse é o caso; leitura útil e interessante.
fonte
Apenas para dar a razão por trás do que todo mundo está dizendo.
A representação binária de um flutuador é meio irritante.
Em binário, a maioria dos programadores conhece a correlação entre 1b = 1d, 10b = 2d, 100b = 4d, 1000b = 8d
Bem, funciona da outra maneira também.
.1b = .5d, .01b = .25d, .001b = .125, ...
O problema é que não há maneira exata de representar a maioria dos números decimais, como .1, .2, .3, etc. Tudo o que você pode fazer é aproximar em binário. O sistema faz um pequeno arredondamento quando os números são impressos para exibir .1 em vez de .10000000000001 ou .999999999999 (que provavelmente estão tão próximos da representação armazenada quanto a .1)
Editar a partir do comentário: a razão pela qual este é um problema são as nossas expectativas. Esperamos totalmente que 2/3 sejam falsificados em algum momento quando o convertermos em decimal, 0,7 ou 0,67 ou 0,6666667 .. Mas não esperamos automaticamente que o valor 1 seja arredondado da mesma maneira que 2/3 --e é exatamente isso que está acontecendo.
A propósito, se você estiver curioso, o número armazenado internamente é uma representação binária pura usando uma "Notação científica" binária. Portanto, se você disser para armazenar o número decimal 10.75d, ele armazenará 1010b para o 10 e 0,11b para o decimal. Assim, ele armazenaria .101011 e, em seguida, salva alguns bits no final para dizer: Mova o ponto decimal quatro casas para a direita.
(Embora tecnicamente não seja mais um ponto decimal, agora é um ponto binário, mas essa terminologia não tornaria as coisas mais compreensíveis para a maioria das pessoas que acharia essa resposta de qualquer utilidade.)
fonte
Porque não é verdade que
0.1 + 0.2 == 0.3
fonte
Float.compare(0.1f+0.2f, 0.3f) == 0
?Eu acho que há muita confusão em torno de carros alegóricos (e duplos), é bom esclarecer isso.
Não há nada inerentemente errado no uso de carros alegóricos como IDs na JVM compatível com o padrão [*]. Se você simplesmente definir o ID da bóia como x, não faça nada com ele (ou seja, sem aritmética) e depois teste para y == x, você estará bem. Além disso, não há nada errado em usá-los como chaves em um HashMap. O que você não pode fazer é assumir igualdades
x == (x - y) + y
, etc. Dito isto, as pessoas geralmente usam tipos inteiros como IDs, e você pode observar que a maioria das pessoas aqui é adiada por esse código; portanto, por razões práticas, é melhor aderir às convenções . Observe que existem tantosdouble
valores diferentes quanto longosvalues
, então você não ganha nada usandodouble
. Além disso, a geração do "próximo ID disponível" pode ser complicada com duplas e requer algum conhecimento da aritmética de ponto flutuante. Não vale a pena.Por outro lado, confiar na igualdade numérica dos resultados de dois cálculos matematicamente equivalentes é arriscado. Isso ocorre devido a erros de arredondamento e perda de precisão ao converter de representação decimal em binária. Isso foi discutido até a morte no SO.
[*] Quando eu disse "JVM em conformidade com os padrões", queria excluir certas implementações de JVM com danos cerebrais. Veja isso .
fonte
==
vez deequals
, ou então garantir que nenhum carro alegórico que se compara de maneira desigual seja armazenado em uma tabela. Caso contrário, um programa que tente, por exemplo, contar quantos resultados únicos podem ser produzidos a partir de uma expressão quando alimentados com várias entradas, pode considerar cada valor de NaN como único.Float
, não afloat
.Float
? Se alguém tentar criar uma tabela defloat
valores exclusivos e compará-los com==
, as horríveis regras de comparação do IEEE-754 resultarão na inundação da tabela deNaN
valores.float
O tipo não possuiequals
método.equals
método de instância, mas o método estático (acho que dentro daFloat
classe) que compara dois valores do tipofloat
.Este é um problema não específico do java. Usar == para comparar dois números flutuantes / duplos / qualquer número decimal pode causar problemas devido à maneira como eles são armazenados. Um flutuador de precisão única (conforme o padrão IEEE 754) possui 32 bits, distribuídos da seguinte forma:
1 bit - Sinal (0 = positivo, 1 = negativo)
8 bits - Expoente (uma representação especial (viés-127) do x em 2 ^ x)
23 bits - Mantisa. O número atual que é armazenado.
A mantisa é o que causa o problema. É como uma notação científica, apenas o número na base 2 (binário) se parece com 1.110011 x 2 ^ 5 ou algo semelhante. Mas, em binário, o primeiro 1 é sempre um 1 (exceto para a representação de 0)
Portanto, para economizar um pouco de espaço na memória (trocadilhos), o IEEE decidiu que o 1 deveria ser assumido. Por exemplo, um mantisa de 1011 é realmente 1.1011.
Isso pode causar alguns problemas na comparação, especialmente com 0, já que 0 não pode ser representado exatamente em um float. Esse é o principal motivo pelo qual o == é desencorajado, além dos problemas matemáticos de ponto flutuante descritos por outras respostas.
Java tem um problema único, pois a linguagem é universal em muitas plataformas diferentes, cada uma das quais poderia ter seu próprio formato de flutuação único. Isso torna ainda mais importante evitar ==.
A maneira correta de comparar dois carros alegóricos (que não são do seu idioma) para a igualdade é a seguinte:
onde ACCEPTABLE_ERROR é # definido ou alguma outra constante igual a 0,000000001 ou qualquer precisão necessária, como Victor já mencionou.
Alguns idiomas têm essa funcionalidade ou essa constante incorporada, mas geralmente esse é um bom hábito.
fonte
A partir de hoje, a maneira mais rápida e fácil de fazer isso é:
No entanto, os documentos não especificam claramente o valor da diferença de margem (um epsilon da resposta do @Victor) que está sempre presente nos cálculos em flutuadores, mas deve ser algo razoável, pois faz parte da biblioteca de idiomas padrão.
No entanto, se for necessária uma precisão mais alta ou personalizada,
é outra opção de solução.
fonte
(sectionId == currentSectionId)
que não é preciso para pontos flutuantes. o método epsilon é a melhor abordagem, que é nesta resposta: stackoverflow.com/a/1088271/4212710Os valores do ponto de foating não são confiáveis devido a erro de arredondamento.
Como tal, eles provavelmente não devem ser usados como valores-chave, como sectionID. Use números inteiros ou
long
seint
não contiver valores possíveis suficientes.fonte
double
s são muito mais precisos, mas também são valores de ponto flutuante; portanto, minha resposta foi incluir ambosfloat
edouble
.Além de respostas anteriores, você deve estar ciente de que existem comportamentos estranhos associados
-0.0f
e+0.0f
(eles são==
, mas nãoequals
) eFloat.NaN
(éequals
, mas não==
) (esperança que eu tenho esse direito - argh, não fazê-lo).Edit: Vamos verificar!
Bem-vindo ao IEEE / 754.
fonte
==
não significa que os números sejam "idênticos ao bit" (o mesmo número pode ser representado com diferentes padrões de bits, embora apenas um deles seja da forma normalizada). Da mesma forma,-0.0f
e0.0f
são representados por diferentes padrões de bits (o bit de sinal é diferente), mas compare como igual a==
(mas não comequals
). Sua suposição de==
comparação bit a bit está, de modo geral, errada.Aqui está uma discussão muito longa (mas espero que útil) sobre esse e muitos outros problemas de ponto flutuante que você pode encontrar: O que todo cientista da computação deve saber sobre aritmética de ponto flutuante
fonte
Você pode usar Float.floatToIntBits ().
fonte
Primeiro de tudo, eles são float ou float? Se um deles for um float, você deve usar o método equals (). Além disso, provavelmente é melhor usar o método estático Float.compare.
fonte
O seguinte usa automaticamente a melhor precisão:
Obviamente, você pode escolher mais ou menos de 5 ULPs ('unidade em último lugar').
Se você estiver na biblioteca do Apache Commons, a
Precision
classe possuicompareTo()
eequals()
com epsilon e ULP.fonte
double
cobrir isso.você pode querer que seja ==, mas 123.4444444444443! = 123.4444444444442
fonte
Se você * tiver que usar flutuadores, a palavra-chave strictfp poderá ser útil.
http://en.wikipedia.org/wiki/strictfp
fonte
Dois cálculos diferentes que produzem números reais iguais não necessariamente produzem números iguais de ponto flutuante. As pessoas que usam == para comparar os resultados dos cálculos geralmente acabam se surpreendendo com isso, então o aviso ajuda a sinalizar o que poderia ser um bug sutil e difícil de reproduzir.
fonte
Você está lidando com código terceirizado que usaria flutuadores para itens denominados sectionID e currentSectionID? Apenas curioso.
@ Bill K: "A representação binária de um flutuador é meio irritante." Como assim? Como você faria isso melhor? Existem certos números que não podem ser representados em nenhuma base adequadamente, porque eles nunca terminam. Pi é um bom exemplo. Você só pode aproximar. Se você tiver uma solução melhor, entre em contato com a Intel.
fonte
Como mencionado em outras respostas, duplos podem ter pequenos desvios. E você pode escrever seu próprio método para compará-los usando um desvio "aceitável". Contudo ...
Há uma classe apache para comparar duplas: org.apache.commons.math3.util.Precision
Ele contém algumas constantes interessantes:
SAFE_MIN
eEPSILON
, que são os desvios máximos possíveis de operações aritméticas simples.Ele também fornece os métodos necessários para comparar, igual ou arredondar o dobro. (usando ulps ou desvio absoluto)
fonte
Em uma resposta de linha, posso dizer, você deve usar:
Para aprender mais sobre o uso correto de operadores relacionados, estou elaborando alguns casos aqui: Geralmente, existem três maneiras de testar cadeias de caracteres em Java. Você pode usar ==, .equals () ou Objects.equals ().
Como eles são diferentes? == testa a qualidade de referência em strings, significando descobrir se os dois objetos são iguais. Por outro lado, .equals () testa se as duas seqüências têm o mesmo valor logicamente. Por fim, Objects.equals () testa quaisquer nulos nas duas seqüências de caracteres e determina se deve chamar .equals ().
Operador ideal para usar
Bem, isso foi sujeito a muitos debates porque cada um dos três operadores tem seu conjunto único de pontos fortes e fracos. Por exemplo, == geralmente é uma opção preferida ao comparar a referência de objeto, mas há casos em que também parece comparar valores de string.
No entanto, o que você obtém é um valor decrescente porque Java cria uma ilusão de que você está comparando valores, mas, no sentido real, não é. Considere os dois casos abaixo:
Caso 1:
Caso 2:
Portanto, é muito melhor usar cada operador ao testar o atributo específico para o qual foi projetado. Mas em quase todos os casos, o Objects.equals () é um operador mais universal, portanto, os desenvolvedores da Web optam por isso.
Aqui você pode obter mais detalhes: http://fluentthemes.com/use-compare-strings-java/
fonte
A maneira correta seria
fonte
Float.compare(1.1 + 2.2, 3.3) != 0
Uma maneira de reduzir o erro de arredondamento é usar o dobro em vez de flutuar. Isso não fará com que o problema desapareça, mas reduz a quantidade de erros no seu programa e o float quase nunca é a melhor escolha. NA MINHA HUMILDE OPINIÃO.
fonte