você está aqui: Home  → Arquivo de Mensagens

Comparando decimais no PHP: a GRANDE pegadinha

Colaboração: André Milani

Data de Publicação: 13 de abril de 2017

Nesta publicação eu vou falar sobre algo que a maioria dos programadores PHP desconhecem ao comparar números decimais, e que pode acabar trazendo uma grande dor de cabeça pra você.

Se você anda comparando números decimais no PHP sem utilizar o comando round, você pode estar passando por sérios apuros! Isso ocorre pois muitos programadores não conhecem a fundo como o PHP lida com este tipo de número e não sabem dos problemas que podem ser causados por falta do uso do comando round. Mas calma, não se preocupe, eu vou explicar para você neste artigo, onde vou abordar duas dicas muito legais sobre PHP. Observe este exemplo:

  <?php
  
     $varA = 0.25;
     $varB = 1 - 0.75; // 0.25
  
     if($varA == $varB)
     {
         $resultadoConta1 = 'valores iguais';
     }
     else
     {
         $resultadoConta1 = 'valores diferentes';
     }
  
     echo 'Conta #1 - Comparando '.$varA.' e '.$varB.': '.$resultadoConta1.'< br>';
     // Conta #1 - Comparando 0.25 e 0.25: valores iguais
  ?>

Até aqui tudo bem, certo? A variável $varA foi inicializada com o número 0.25 e a variável $varB foi inicializada com o resultado de 1 - 0.75, que é 0.25. A comparação é realizada e é impresso na tela que os valores são iguais.

Agora vamos observar o mesmo código PHP, porém com outros números de exemplo, mas que também deveriam ser iguais, como por exemplo $varA sendo inicializada com 0.23 e $varB inicializada com 1 - 0.77, que é 0.23:

  <?php
  
     $varA = 0.23;
     $varB = 1 - 0.77; // 0.23
  
     if($varA == $varB)
     {
         $resultadoConta2 = 'valores iguais';
     }
     else
     {
         $resultadoConta2 = 'valores diferentes';
     }
  
     echo 'Conta #2 - Comparando '.$varA.' e '.$varB.': '.$resultadoConta2.'< br>';
     // Conta #2 - Comparando 0.23 e 0.23: valores diferentes
  ?>

Pois é. Se você executou este código você pôde comprovar na prática que o PHP, neste caso, imprimirá na tela que os valores 0.23 e 0.23 são diferentes, mesmo sendo iguais aos nossos olhos.

Isso ocorre porque, internamente, o PHP não arredonda automaticamente as casas decimais em suas operações matemáticas, e se isso for somado ao fato dos processadores dos computadores apresentarem pontos flutuantes em algumas operações, certamente essa flutuação (ou variação, se preferir) aparecerá em algum momento se não for corrigida.

O lado bom de conhecer como as coisas funcionam internamente é que você pode solucionar eventuais problemas como este, e fazer isso no PHP é muito fácil: basta forçar o arredondamento das casas decimais, baseado no número de casas que você estiver trabalhando, por meio do comando round.

A sintaxe do comando round é muito simples: round($valor, $precisao), onde o parâmetro $valor deve ser o seu número decimal, e $precisao o número de casas para o arredondamento. Pronto, com isso o próprio PHP arredonda os números, eliminando a flutuação indesejada. Veja como este mesmo código pode ser corrigido, mesmo com os valores que apresentaram problemas:

  <?php
  
     $varA = round(0.23, 2);
     $varB = round(1 - 0.77, 2); // 0.23
  
     if($varA == $varB)
     {
         $resultadoConta3 = 'valores iguais';
     }
     else
     {
         $resultadoConta3 = 'valores diferentes';
     }
  
     echo 'Conta #3 - Comparando '.$varA.' e '.$varB.': '.$resultadoConta3.'< br>';
     // Conta #3 - Comparando 0.23 e 0.23: valores iguais
  ?>

Se você gostou dessa dica e tem interesse em aprender a programar em PHP, quero lhe fazer um convite: já estão abertas a inscrições para o PHP Academy, o maior evento online e gratuito de PHP do Brasil, realizado pela Softblue. Este evento é totalmente dedicado ao ensino do PHP para quem quer começar a trabalhar com essa. Inscreva-se GRAUITAMENTE agora mesmo clicando aqui.



 

 

Veja a relação completa dos artigos de André Milani

Opinião dos Leitores

Fernando Roxo
13 Abr 2017, 12:57
Esse é um problema comum para quem trabalha com processamento numérico em ponto flutuante, por exemplo em Fortran.

Quando trabalhamos com ponto flutuante fazemos comparações de maior ou menor, igual não fazemos por ser incerto e nem sempre reprodutível.

Normalmente quando queremos testar a igualdade o que fazemos é assumir que diferenças menores que um certo limiar são aceitos como igualdade. Por exemplo:

if ( abs(v1-v2) < 1.e-6 ) then iguais
else diferentes

O valor 1.e-6 não é gratuito, um número em PF de precisão simples tem 5,5 dígitos significativos (não, não vou explicar o 0,5. ;) ) portanto um resíduo menor que a resolução pode ser aceitável como sinal de igualdade.

Em alguns casos a diferença deve ser normalizada, a depender da ordem de grandeza dos valores. Deixo isso como dever de casa. :P

Alexandre Heitor Schmidt
13 Abr 2017, 09:01
Para complementar o assunto, recomendo a leitura sobre números de ponto flutuante no site do PHP:

http://php.net/manual/en/language.types.float.php

Neste mesmo link há um link externo para http://floating-point-gui.de/, cujo objetivo é fornecer explicações mais concisas sobre porquê isso acontece.
*Nome:
Email:
Me notifique sobre novos comentários nessa página
Oculte meu email
*Texto:
 
  Para publicar seu comentário, digite o código contido na imagem acima
 


Powered by Scriptsmill Comments Script