Executando verificação de segurança...
2

As linguagens de programação não conseguem fazer esse simples calculo! Veja

Alguém consegue me explicar o porquê isso acontece?


print(128.23 - (118.23 + 10)

echo 128.23 - (118.23 + 10);
 console.log(128.23 - (118.23 + 10.00))

Linguaguens usadas para teste: PHP, Python, JavaScript

Ao invés de retornar 0 ou até 0.00, estão retornando -2.842170943040401e-14

Carregando publicação patrocinada...
6

Na maioria das (senão em todas as) linguagens mainstream, os números de ponto flutuante seguem a norma IEEE 754.

Este padrão possui uma série de problemas de imprecisão, por causa da forma como foi definido. No caso de um double, o formato é:

  • 1 bit para o sinal
  • expoente: inteiro de 11 bits, para indicar o valor do expoente deslocado 1023 unidades, ou então um dos dois valores com significados especiais: 0x000 para valores subnormais e zero; 0x7FF para representar infinito e NaN. (i.e. 2x - 1023, onde x é o valor inteiro do campo)
  • mantissa: 52 bits, para valores normais do expoente, representa um valor racional que vai de 1.0 inclusive até 2.0 exclusive, matematicamente [1, 2[. Para o valor do expoente 0x000, então representa valores subnormais (i.e. menores que o menor valor normal representável) ou zero; Para o valor do expoente 0x7FF representa infinito se for 0, ou NaN se for diferente de 0.

O problema é que a norma IEEE 754 tenta representar as frações na base 2, e isso não é suficiente para representar todos os valores possíveis na base 10. O máximo que ele pode fazer são aproximações.

Por exemplo, o número 0.1 na base decimal é representado como 1 x 10-1. Mas na base 2, não tem como representá-lo com exatidão. Se usarmos uma casa decimal, (0.1, que seria 1 x 2-1) o resultado equivale a 0.5 em decimal (nada próximo de 0.1). Se usarmos 8 casas decimais, ficaria 1.10011001 x 2-4, que equivale a 0.099853515625 em decimal. Mais próximo de 0.1, mas ainda não é exato. Mesmo se usarmos 23 casas decimais, teremos 1.10011001100110011001101 × 2-4, que é equivalente a 0.100000001490116119384765625 em decimal. Próximo de 0.1, mas ainda sim inexato.

E não adianta, por mais que adicionemos casas decimais na base 2, o máximo que conseguimos é aproximar um pouco mais o valor. Mas muitos valores jamais poderão ser representados com exatidão.

Não tem jeito de "resolver", é algo intrínseco à norma. Se quer mais precisão, precisa usar bibliotecas dedicadas. Muitas linguagens possuem tipos como Decimal, BigDecimal, etc. Outras não têm, e só é possível com bibliotecas externas.

Para saber mais, veja aqui, aqui e aqui.


Como vc mencionou Python, uma alternativa é usar o módulo decimal, feito justamente para contornar os problemas de precisão do float:

from decimal import Decimal as d

print(d('128.23') - (d('118.23') + d('10'))) # 0.00

Em PHP e JavaScript não tem nada nativo, então somente com bibliotecas externas.

1
2

O computador não lida muito bem com ponto flutuante (números reais, fracionados).
Esse resultado estranho, se não me engando, é notação científica, é uma aproximação do resultado encontrado: -2.842170943040401 elevado a -14

1
1

tem várias técnicas diferentes que a gente pode usar, algumas linguagens tem libs ou funções que auxiliam nisso (aí tem que vasculhar as documentações). no meu caso, das soluções que testei, a que mais gostei e tive melhor resultado foi trabalhar com inteiros, e é a mesma coisa pra qualquer linguagem

1

Se quer trabalhar com valores exatos, pode usar outras libs específicas (como já mencionei na outra resposta).

Mas se for para continuar com float, o que vc pode fazer é verificar se o valor está próximo de zero, com alguma tolerância.

Em Python, por exemplo, pode usar math.isclose:

from math import isclose

x = 128.23 - (118.23 + 10)
if isclose(x, 0, rel_tol=1e-10, abs_tol=1e-10):
    print('ok') # entra no if

Os parâmetros rel_tol e abs_tol são a tolerância que vc usa para comparar os números (consulte a documentação para mais detalhes). Não tem muito jeito, tem que ficar ajustando os valores para a precisão que você vai trabalhar.

Em JavaScript, pode ser feito algo similar. Uma versão bem simplificada seria:

function iguais(a, b, tolerancia=1e-10) {
    return Math.abs(a - b) <= tolerancia;
}

var x = 128.23 - (118.23 + 10.00);
console.log(iguais(x, 0)); // true
1

Fala samuhmatos!

Testando apenas a operação dentro dos parêntesis
>>> 118.23+10

o resultado obtido é: 128.23000000000002

Aplicando um round na operação:

>>> 128.23 - (round(118.23 + 10,2))

o resultado obtido é 0.0

Mas o resultado incorreto provém realmente da norma aplicada para ponto flutuante conforme o kht explicou muito bem!