Números de ponto flutuante
Números de ponto flutuante (também conhecidos como "floats", "doubles" ou "números reais"), podem ser especificados utilizando qualquer uma das seguintes sintaxes:
<?php
$a = 1.234;
$b = 1.2e3;
$c = 7E-10;
$d = 1_234.567; // a partir do PHP 7.4.0
?>
Formalmente a partir do PHP 7.4.0 (anteriormente, sublinhados não eram permitidos):
LNUM [0-9]+(_[0-9]+)* DNUM ({LNUM}?"."{LNUM}) | ({LNUM}"."{LNUM}?) EXPONENT_DNUM (({LNUM} | {DNUM}) [eE][+-]? {LNUM})
O tamanho de um número de ponto flutuante depende da plataforma, sendo o máximo de ~1.8e308 com precisão de 14 dígitos decimais um valor comum (número de 64 bits no formato IEEE).
Precisão de números de ponto flutuante
Números de ponto flutuante tem precisão limitada. Embora dependa do sistema, o PHP geralmente utiliza o formato de precisão dupla do IEEE 754, que trará uma precisão máxima devida a arredondamentos da ordem de 1.11e-16. Operações matemáticas incomuns poderão ocasionar erros maiores, e, claro, a propagação de erros deve ser considerada quando várias operações forem realizadas.
Além disso, números racionais que têm representação exata em números
em base 10, como 0.1
ou
0.7
, não possuem representação exata em ponto flutuante
na base 2, o formato utilizado internamente, não importando o tamanho
da mantissa. Portanto não existe conversão para o formato interno sem
uma pequena perda de precisão. Isso pode ocasionar resultados
confusos: por exemplo, floor((0.1+0.7)*10)
normalmente
retornará 7
, em vez do resultado esperado 8
,
porque a representação interna final será algo como
7.9999999999999991118...
.
Então, nunca confie em resultados com números de ponto flutuante até a última casa, e nunca compare números de ponto flutuante em igualdades. Se você realmente precisar de alta precisão, você pode utilizar as funções matemáticas de precisão arbitrária e as funções gmp estão disponíveis.
Para uma explicação "simples" dessa questão, veja o » guia sobre ponto flutuante, que também tem o título alternativo de "Porque meus números não somam direito?".
Convertendo para float
A partir de strings
Se a string é
numérica
ou começa numérica então ela irá resolver para o
valor float correspondente, do contrário ela é convertida em zero
(0
).
A partir de outros tipos
Para valores de outros tipos, a conversão é realizada convertendo o valor para int primeiro e em seguida para float. Veja Convertendo para inteiro para mais informações.
Note:
Como certos tipos têm comportamento indefinido ao converter para int, este também é o caso ao converter para float.
Comparando floats
Como notado acima, testar números de ponto flutuante com igualdade é problemático, por causa da maneira como são representados internamente. Entretanto existem maneiras de fazer comparações com números de ponto flutuante que contornam essas limitações.
Para testar números de ponto flutuante, utilize um "valor de erro máximo" na comparação utilizada. Esse valor é também chamado de epsilon, ou unidade de erro, e deve ser a diferença mínima aceitável no resultado dos cálculos.
$a e $b serão consideradas iguais até o 5º dígito de precisão.
<?php
$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;
if(abs($a-$b) < $epsilon) {
echo "iguais";
}
?>
NaN
Algumas operações numéricas podem resultar em valores representados pela constante
NAN
. Esse resultado representa um valor desconhecido
ou não representável nos cálculos de ponto flutuante. Qualquer comparação frouxa
ou restrita deste valor com qualquer outro, inclusive ele mesmo, com exceção de true
, terá
como resultado false
.
Como o NAN
representa um resultado irrepresentável,
NAN
não deve ser comparado com outros valores, incluindo
ele mesmo, em vez disso, deve-se checá-lo utilizando a função is_nan().