O valor exato de float 0.7f
é 0.69999...
então pensei que o resultado 0.7f * 100f
seria algo abaixo 70
, como 69.99999...
Mas o resultado é exato 70
.
A multiplicação de float envolve tal arredondamento?
Em caso afirmativo, esse pós-processamento também é aplicável em ponto fixo?
Encontrei em algumas bibliotecas de ponto fixo, FP(100) * FP(0.7) is 69.99999
.
Ao lançar isso para int, eles truncam impiedosamente e eu recebo 69
. O que é indesejável, pois o FP pode expressar exato 70
.
O ponto flutuante binário de precisão finita não pode representar exatamente todos os números reais (ou decimais), mas deve sempre representar o número mais próximo possível .
A aritmética de ponto flutuante de precisão finita também não pode calcular todos os resultados possíveis, mas é necessária (pelo menos em casos comuns) para calcular um resultado devidamente arredondado.
O ponto flutuante de precisão simples exato (representado mais próximo) correspondente a 0,7 é
0b0.101100110011001100110011
, que quando convertido novamente em decimal é 0,699999988079071044921875. Observe que esse número possui 24 bits significativos, o que faz parte da definição de precisão única IEEE-754.Multiplicar esse número por 100 nos daria
0b1000101.1111111111111111111011
. Mas esse número tem 29 bits significativos, portanto não cabe nos 24 bits de significância disponíveis em precisão única. Então temos que arredondar. Agora, o 25º bit é 1, então arredondamos para cima, e quase todos os bits à esquerda desse 25º bit são 1, então ele arredonda para cima0b1000110.00000000000000000
, ou exatamente 70,0.Observe que, embora o ponto flutuante de precisão simples tenha 24 bits de significância, os cálculos envolvendo ponto flutuante de precisão simples devem ser realizados usando, temporariamente, mais de 24 bits de precisão, para que alguns bits além do 24 possam ser computados, para que o resultado pode ser arredondado corretamente, conforme necessário.
E este é um bom exemplo de como as regras do IEEE-754 sobre resultados arredondados funcionam, e funcionam bem. É fácil ter a impressão (equivocada) de que os valores de ponto flutuante estão sempre pelo menos um pouco errados, se não totalmente quebrados . Mas, na verdade, a aritmética de ponto flutuante IEEE-754 geralmente é bastante precisa e se esforça para evitar que os erros se agravem - o que significa que, não raramente, os erros podem se anular, produzindo resultados exatos, afinal. Isso é basicamente o que acontece aqui.
Ou, em outras palavras, a regra não é que os cálculos de ponto flutuante sejam sempre imprecisos. A verdadeira regra é que os cálculos de ponto flutuante às vezes são imprecisos – mas às vezes também são perfeitamente precisos.
(Na verdade, uma regra ainda melhor é que os cálculos de ponto flutuante são muitas vezes imprecisos em comparação com um resultado esperado, mas decimal . Se você pegar dois números binários de ponto flutuante e fizer uma operação sobre eles, quase sempre obterá um resultado que é realmente preciso - em binário O que quero dizer é que a maioria das imprecisões aparentes ocorre apenas quando você compara o resultado binário com um que você calculou, de alguma outra forma, em decimal.)
Se a explicação acima não funcionar para você, aqui está outra maneira de ver a questão. Como sabemos, as representações de ponto flutuante não podem representar todos os números. Na verdade, um dos números que eles não podem representar em binário é 0,7, e o número representável mais próximo em precisão simples é um número binário igual a 0,699999988079071044921875.
Agora, e se pegarmos esse número 0,699999988079071044921875 e multiplicá-lo por 100? O resultado exato deve ser 69,9999988079071044921875. Mas este é outro número que não pode ser representado com exatidão. Se você pegar esse número não representável 69,9999988079071044921875 e perguntar qual seria o número mais próximo que pode ser representado exatamente com precisão simples, esse número é... 70,0! Em precisão simples, o próximo número representável menor que esse é 69,99999237060546875, que está mais distante (neste caso, mais de 5 vezes mais distante) do que 70,0.
Você também perguntou sobre bibliotecas de ponto fixo. Aí a resposta dependeria obviamente da implementação, mas mais importante ainda, da base. Uma biblioteca binária de ponto fixo também não pode representar exatamente 0,7. Mas uma biblioteca decimal de ponto fixo obviamente poderia.
Uma representação binária de ponto fixo "16,16" de 0,7 seria, eu acho,
0000000010110011
(base 2), ou00b3
(base 16) ou 179 (base 10), que convertida novamente em uma fração é 0,69921875. Multiplicar isso por 100 dá100010111101100
//45ec
17900, que converte novamente para 69,921875. Portanto, sob essas suposições, não vejo uma maneira de fazer melhor (ou seja, uma maneira de obter 70,0).Mas se uma biblioteca decimal de ponto fixo fornecesse 69 após a multiplicação, eu diria que está muito mal implementada. Uma representação decimal de ponto fixo de 16 bits de 0,7 com um fator de escala de 100 seria 70, que quando multiplicado por 100 dá 7.000, que quando dividido pelo fator de escala novamente obviamente dá exatamente 70,0.