← Voltar aos artigos

float · números · precisão · comparação · bcmath

Por que 0.1 + 0.2 não é igual a 0.3 no PHP

Publicado em 22/07/2026 · 6 min de leitura

No PHP, 0.1 + 0.2 == 0.3 devolve false — e não é bug do PHP, é como todo computador guarda número decimal. Floats seguem o padrão IEEE 754, que armazena os números em binário, e alguns decimais simples (como 0.1) não têm representação exata em binário — do mesmo jeito que 1/3 não tem representação exata em decimal (0.3333… não termina nunca). O resultado é que 0.1 + 0.2 não é exatamente 0.3, e por isso a comparação direta falha. A regra que resolve: nunca compare floats com == direto — compare por tolerância, e para dinheiro nem use float. Vamos ao porquê e ao como.

O mecanismo: por que 0.1 não cabe em binário

Em decimal, você escreve frações como somas de potências de 10: 0.1 é 1/10. Em binário, o computador só tem potências de 2 (1/2, 1/4, 1/8, 1/16…). Alguns decimais caem certinho — 0.5 é 1/2, exato. Mas 0.1 em binário vira uma dízima infinita e periódica: 0.0001100110011001100… sem fim. Como o float (double de 64 bits) tem espaço finito, o PHP guarda a versão arredondada mais próxima — não o 0.1 exato.

Aí a conta acumula erro: o 0.1 guardado é um tiquinho maior, o 0.2 também, e a soma passa levemente de 0.3. O 0.3 guardado, por sua vez, é um tiquinho menor que 0.3. Dois valores minimamente diferentes → == retorna false. Não há bug: cada operação está fazendo exatamente o que o IEEE 754 manda.

Isso vale para qualquer linguagem que use IEEE 754 — JavaScript, Python, Java, C. Não é uma falha do PHP; é a física do ponto flutuante.

Diagnóstico: enxergue o valor de verdade

Aqui está a parte que mais confunde o iniciante — e o teste que esclarece tudo:

echo 0.1 + 0.2;   // 0.3   ← parece certo!
var_dump(0.1 + 0.2 == 0.3);   // bool(false)   ← mas não é

Por que o echo mostra 0.3 se o valor não é 0.3? Porque o PHP tem uma configuração precision (padrão 14 dígitos) que arredonda o número só na hora de exibir. O valor guardado na memória continua "errado"; a tela é que mente para você. Para ver o número real, force mais casas com printf:

printf("%.17f\n", 0.1 + 0.2);   // 0.30000000000000004
printf("%.17f\n", 0.3);         // 0.29999999999999999

Agora dá para ver os dois valores lado a lado e entender o false. Sempre que um float "deveria bater" e não bate, imprima com %.17f — em segundos você confirma que é o erro de representação, não lógica sua.

A solução: comparar com tolerância (epsilon)

Como os dois valores diferem por uma migalha, a comparação correta não é "são idênticos?", e sim "a diferença entre eles é menor que uma margem desprezível?". Essa margem chama-se epsilon. O PHP já traz uma constante para isso, PHP_FLOAT_EPSILON (≈ 2.2e-16, o menor passo representável perto de 1.0):

evite
$soma = 0.1 + 0.2;

if ($soma == 0.3) {          // false — quase nunca é o que você quer
    echo "igual";
}
prefira
$soma = 0.1 + 0.2;

if (abs($soma - 0.3) < PHP_FLOAT_EPSILON) {   // true
    echo "igual (dentro da tolerância)";
}

A ideia é sempre a mesma: abs($a - $b) < $tolerancia. O abs() (valor absoluto) garante que funciona independentemente de qual é maior.

Antecipando a próxima pergunta — e se os números forem grandes? PHP_FLOAT_EPSILON só serve para valores perto de 1.0. Floats perdem precisão conforme crescem, então para 1000000.1 + 1000000.2 a diferença real é bem maior que 2.2e-16 e a comparação daria false de novo. Nesses casos, escale a tolerância pela magnitude dos números (epsilon relativo):

function floatsIguais(float $a, float $b, float $epsilon = 1e-9): bool {
    return abs($a - $b) <= $epsilon * max(1.0, abs($a), abs($b));
}

Para a maioria dos casos do dia a dia, um epsilon fixo pequeno como 1e-9 já resolve; use o relativo quando lidar com ordens de grandeza muito diferentes.

A armadilha central

Dinheiro: a exceção que não admite float nenhum

Para valores monetários, epsilon não basta — você não quer nenhum erro, nem de um centésimo de centavo, e ele se acumula a cada operação. A regra é: dinheiro nunca vai em float. Duas saídas:

1. Guarde em inteiros (centavos). Em vez de R$ 19,90 como 19.90, guarde 1990 (centavos). Inteiros são exatos e a aritmética não tem erro de representação; você só divide por 100 na hora de exibir. É a abordagem mais simples e usada em muito sistema de pagamento.

$precoEmCentavos = 1990;          // R$ 19,90
$total = $precoEmCentavos * 3;    // 5970 → exato
printf("R$ %.2f", $total / 100);  // R$ 59,70 (divide só para mostrar)

2. Use BCMath para casas decimais arbitrárias. Quando precisa de frações (juros, câmbio, impostos), a extensão BCMath faz aritmética em base decimal, com a precisão que você pedir, operando sobre strings (para não passar pelo float em nenhum momento):

echo bcadd('0.1', '0.2', 1);   // 0.3  ← exato, sem epsilon
echo bcmul('19.90', '3', 2);   // 59.70

Onde vai cada abordagem

Situação Faça Por quê
Comparar dois floats abs($a - $b) < $epsilon == falha por erro de representação
Floats de magnitude grande/variada Epsilon relativo (escala pelo maior) Precisão do float cai quando o número cresce
Ver o valor "de verdade" printf("%.17f", $x) O echo arredonda em precision e engana
Dinheiro (só somar/multiplicar) Inteiros em centavos Inteiro é exato, sem IEEE 754
Dinheiro com frações (juros, câmbio) BCMath sobre strings Aritmética decimal com precisão definida

O resumo cabe numa linha: float é ótimo para medidas e cálculos científicos onde uma migalha não importa; para igualdade use tolerância; para dinheiro, saia do float.

no manualFloating point numbers

A página oficial dos números de ponto flutuante explica os limites de precisão do IEEE 754 e por que a comparação de igualdade exige cuidado — a base de tudo neste artigo.

estudar na árvore →
no manualBC Math Functions

As funções BCMath são o kit do PHP para aritmética decimal exata (dinheiro, juros, qualquer conta que não pode errar um centavo). Vale conhecer bcadd, bcmul, bccomp e a escala.

estudar na árvore →

Leve o estudo além do artigo

O manual inteiro do PHP como uma jornada: progresso por tópico, anotações e leitura embutida.