Sendo justo: Todas as linguagens tem dificuldade com ponto flutuante, mas eu penso que o PHP exagera no negócio.
Veja esse cálculo:
Vindo de um software para lançamentos contábeis, o PHP se perde no centésimo, veja, não estamos pedindo para calcular Pi até milhonésima casa decimal, são centavos, centésimos!
Quando preciso fazer cálculos precisos em PHP, uso uma dessas abordagens:
BCMath
BCMath (PHP: BC Math – Manual), que é uma biblioteca que traz precisão matemática aos cálculos no PHP. Em regra ela está habilitada no servidor, se não estiver, o manual ensina como fazer.
Em todo o caso, antes de tentar essa abordagem:
if (extension_loaded('bcmath')) {
echo 'A extensão BCMath está habilitada.';
} else {
echo 'A extensão BCMath não está habilitada.';
}
Estando carregada, a aplicação:
// Certifique-se de trabalhar com strings para alta precisão
$soma_debito = '500';
$soma_credito = '499.99';
// Use bcsub para subtrair os valores
$diferenca = bcsub($soma_debito, $soma_credito, 2); // O '2' indica o número de casas decimais
// Agora, você pode comparar a diferença com uma string que representa 'zero' com precisão
if (bccomp($diferenca, '0', 2) !== 0) {
$erros->inclui_erro("Os débitos diferem dos créditos. Debitos: {$soma_debito}, Créditos: {$soma_credito}, Diferença:{$diferenca}");
} elseif (bccomp($soma_debito, $valor, 2) !== 0) {
$erros->inclui_erro('Valor está diferente dos débitos/créditos');
}
E pronto, temos a desejada precisão.
Transformar em inteiros
Quando trabalho com moeda, sendo as casas decimais fixas, em cálculos simples, os transformo em inteiros e tenho a precisão ideal:
$soma_debito = 50000; // R$ 500,00 em centavos
$soma_credito = 49999; // R$ 499,99 em centavos
// Aqui você compara diretamente os valores inteiros
if ($soma_debito <> $soma_credito) {
$diferenca = $soma_debito - $soma_credito; // A diferença será um valor inteiro em centavos
$erros->inclui_erro("Os débitos diferem dos créditos. Debitos: {$soma_debito}, Créditos: {$soma_credito}, Diferença: {$diferenca}");
} elseif ($soma_debito <> $valor) {
$erros->inclui_erro('Valor está diferente dos débitos/créditos');
}
Um esquema simples é usar number format (PHP: number_format – Manual), eliminando em seguida com expressão regular os pontos e traços, trabalhando com os inteiros.
Quando for seguir o processamento, coloque novamente o marcador de decimal nas duas últimas e casas e siga feliz.
Essa segunda é mais trabalhosa e mais limitada, só use caso seja impossível a primeira abordagem com BCMath.
Serviu para ti? Deixe um comentário, conte sua experiência. Tem uma solução melhor? Compartilhe.
Deixe um comentário