Eu quero comparar dois carros alegóricos em PHP, como neste código de exemplo:
$a = 0.17;
$b = 1 - 0.83; //0.17
if($a == $b ){
echo 'a and b are same';
}
else {
echo 'a and b are not same';
}
Neste código retorna o resultado da else
condição em vez da if
condição, mesmo que $a
e $b
são os mesmos. Existe alguma maneira especial de manipular / comparar carros alegóricos em PHP?
Se sim, por favor me ajude a resolver esse problema.
Ou há algum problema com a configuração do meu servidor?
php
floating-point
Santosh Sonarikar
fonte
fonte
a and b are same
. Esse é o seu código completo?floating-point
descrição da tag? stackoverflow.com/tags/floating-point/info Esse é um comportamento que você provavelmente encontrará em qualquer linguagem de programação ao usar números de ponto flutuante. Veja, por exemplo, stackoverflow.com/questions/588004/is-javascripts-math-brokenRespostas:
Se você fizer assim, eles devem ser os mesmos. Mas observe que uma característica dos valores de ponto flutuante é que os cálculos que parecem resultar no mesmo valor não precisam ser realmente idênticos. Portanto, se
$a
é um literal.17
e$b
chega lá através de um cálculo, pode ser que eles sejam diferentes, embora ambos exibam o mesmo valor.Normalmente, você nunca compara valores de ponto flutuante para igualdade como esta; é necessário usar a menor diferença aceitável:
Algo parecido.
fonte
abs($a-$b)
>abs(($a-$b)/$b)
0.10000000000000000555111512312578270211815834045410156
geralmente não faz sentido e eles preferem0.1
. E escrevendo um número para que possa ser lido novamente exatamente da mesma maneira. Como você vê, não é tão nítido quanto você imagina. E para o registro, você ainda quiser comparar números de ponto flutuante como eu têm mostrado porque você pode chegar$a
e$b
através de diferentes cálculos que podem torná-los diferentes.a=b=0
e sea
for o menor possível, nenhum valor positivo zero eb
o menor valor negativo não zero possível, o teste falhará incorretamente. Algumas informações bom aqui: floating-point-gui.de/errors/comparison$b
? o manual do PHP apenas fizif(abs($a-$b) < $epsilon)
o manual do MySQL também fez o mesmoHAVING ABS(a - b) <= 0.0001
$a == $b == 0
, mas já é muito mais geral do que erro absoluto. Se$a
e$b
está na casa dos milhões, então vocêEPSILON
teria que ser muito diferente do que se$a
e$b
estiver em algum lugar próximo0
. Consulte o link de Dom acima para uma melhor discussão sobre isso.Leia o aviso vermelho no manual primeiro. Você nunca deve comparar carros alegóricos pela igualdade. Você deve usar a técnica epsilon.
Por exemplo:
onde
PHP_FLOAT_EPSILON
é constante representando um número muito pequeno (você deve defini-lo nas versões antigas do PHP antes da 7.2)fonte
EPSILON
aqui está uma constante definida pelo usuário arbitrariamente. O PHP não possui uma constante interna que representa a idéia específica de epsilon de uma arquitetura. (Veja tambémget_defined_constants
.)PHP_FLOAT_EPSILON
O menor número positivo representável x, de modo que x + 1,0! = 1,0. Disponível a partir do PHP 7.2.0.echo $a - $b; /* 5.6843418860808E-14 */ echo PHP_FLOAT_EPSILON; /* 2.2204460492503E-16 */
A questão é como você deseja definir "igual" para o seu aplicativo, quão próximo os números devem ser considerados iguais.Ou tente usar as funções matemáticas bc:
Resultado:
fonte
bccomp()
aceita strings como argumentos. De qualquer forma, você pode usarPHP_FLOAT_DIG
para o argumento de escala.Como dito anteriormente, tenha muito cuidado ao fazer comparações de ponto flutuante (igual a, maior que ou menor que) em PHP. No entanto, se você estiver interessado apenas em alguns dígitos significativos, poderá fazer algo como:
O uso do arredondamento para 2 casas decimais (ou 3 ou 4) causará o resultado esperado.
fonte
loose_float_compare
esse, para que fique óbvio o que está acontecendo.bccomp($a, $b, 2)
é superior à sua solução. Neste exemplo, o 2 é a precisão. você pode configurá-lo para qualquer número de pontos flutuantes que deseja comparar.Seria melhor usar a comparação nativa do PHP :
fonte
Se você tiver valores de ponto flutuante para comparar com igualdade, uma maneira simples de evitar o risco de estratégia de arredondamento interno do sistema operacional, idioma, processador ou assim por diante, é comparar a representação de string dos valores.
Você pode usar qualquer um dos seguintes para produzir o resultado desejado: https://3v4l.org/rUrEq
Fundição do tipo String
Concatenação de String
função strval
As representações de strings são muito menos complicadas do que as flutuações quando se trata de verificar a igualdade.
fonte
(string)
operação de conversão é executada por referência, alterando a declaração original? Nesse caso, não é esse o caso 3v4l.org/CraasSe você tiver um número pequeno e finito de casas decimais aceitáveis, o seguinte funcionará bem (embora com desempenho mais lento que a solução epsilon):
fonte
Isso funciona para mim no PHP 5.3.27.
fonte
Para o PHP 7.2, você pode trabalhar com o PHP_FLOAT_EPSILON ( http://php.net/manual/en/reserved.constants.php ):
fonte
==
e!=
, mas não>
,>=
,<
,<=
Se você escrever assim, provavelmente funcionará, então imagino que você o simplificou para a pergunta. (E manter a pergunta simples e concisa é normalmente uma coisa muito boa.)
Mas neste caso, imagino que um resultado é um cálculo e um resultado é uma constante.
Isso viola uma regra fundamental da programação de ponto flutuante: nunca faça comparações de igualdade.
As razões para isso são um pouco sutis 1, mas o que é importante lembrar é que elas geralmente não funcionam (exceto, ironicamente, por valores integrais) e que a alternativa é uma comparação nebulosa ao longo das linhas de:
1. Um dos principais problemas envolve a maneira como escrevemos números em programas. Nós as escrevemos como cadeias decimais e, como resultado, a maioria das frações que escrevemos não possui representações exatas da máquina. Eles não têm formas finitas exatas porque se repetem em binário. Toda fração de máquina é um número racional da forma x / 2 n . Agora, as constantes são decimais e cada constante decimal é um número racional da forma x / (2 n * 5 m ). Os números de 5 m são ímpares, portanto, não há um fator 2 n para nenhum deles. Somente quando m == 0 há uma representação finita na expansão binária e decimal da fração. Portanto, 1,25 é exato porque é 5 / (2 2 * 5 0), mas 0,1 não é porque é 1 / (2 0 * 5 1 ). De fato, na série 1.01 .. 1,99, apenas 3 dos números são exatamente representáveis: 1,25, 1,50 e 1,75.
fonte
round($float, 3) == round($other, 3)
Aqui está a solução para comparar pontos flutuantes ou números decimais
Lance uma
decimal
variável parastring
e você ficará bem.fonte
A comparação de flutuadores para igualdade possui um algoritmo O (n) ingênuo.
Você deve converter cada valor de flutuação em uma sequência e comparar cada dígito começando no lado esquerdo da representação de sequência de cada flutuação usando operadores de comparação de números inteiros. O PHP autocast o dígito em cada posição do índice para um número inteiro antes da comparação. O primeiro dígito maior que o outro interromperá o loop e declarará o float ao qual ele pertence como o maior dos dois. Em média, haverá comparações de 1/2 * n. Para carros alegóricos iguais entre si, não haverá n comparações. Este é o pior cenário para o algoritmo. O melhor cenário é que o primeiro dígito de cada flutuador seja diferente, causando apenas uma comparação.
Você não pode usar OPERADORES DE COMPARAÇÃO INTEIRO em valores flutuantes brutos com a intenção de gerar resultados úteis. Os resultados dessas operações não têm significado porque você não está comparando números inteiros. Você está violando o domínio de cada operador que gera resultados sem sentido. Isso vale para comparação delta também.
Use operadores de comparação de números inteiros para o que eles foram projetados: comparar números inteiros.
SOLUÇÃO SIMPLIFICADA:
fonte
2019
TL; DR
Use minha função abaixo, assim
if(cmpFloats($a, '==', $b)) { ... }
cmpFloats($a, '<=', $b)
vsbccomp($a, $b) <= -1
Resumo
Vou desvendar o mistério.
Portanto, se você tentar o abaixo, será igual:
Como obter o valor real de float?
Como você pode comparar?
==
e!=
, você pode convertê-los em strings, deve funcionar perfeitamente:Digite elenco com string :
Ou tipecast com
number_format()
:Aviso:
Evite soluções que envolvam manipular carros alegóricos matematicamente (multiplicar, dividir etc.) e depois compará-los; em geral, eles resolverão alguns problemas e introduzirão outros.
Solução sugerida
Eu criei a função PHP pura (sem dependências / bibliotecas / extensões necessárias). Verifica e compara cada dígito como sequência. Também funciona com números negativos.
fonte
A função do @evilReiko possui alguns erros como estes:
Na minha função, eu corrigi esses erros, mas de qualquer maneira, em alguns casos, essa função retorna respostas erradas:
Função fixa para comparar carros alegóricos
Responda a sua pergunta
fonte
Aqui está uma classe útil da minha biblioteca pessoal para lidar com números de ponto flutuante. Você pode ajustá-lo ao seu gosto e inserir qualquer solução que desejar nos métodos de classe :-).
fonte
Resposta simples:
fonte