Eu imagino que seja uma pergunta clássica de precisão de ponto flutuante, mas estou tentando entender esse resultado, rodando 1//0.01
nos rendimentos do Python 3.7.5 99
.
Imagino que seja um resultado esperado, mas existe alguma maneira de decidir quando é mais seguro usar int(1/f)
do que 1//f
?
round()
e nunca//
ouint()
. A questão vinculada é sobre a comparação de flutuação não tem nada a ver com truncamento e não tem uma solução tão fácil.Respostas:
Se fosse uma divisão com números reais,
1//0.01
seria exatamente 100. Já que são aproximações de ponto flutuante,0.01
é um pouco maior que 1/100, o que significa que o quociente é um pouco menor que 100. É esse valor de 99. algo que é calculado a 99.fonte
As razões para esse resultado são como você declara e são explicadas em A matemática do ponto flutuante está quebrada? e muitas outras perguntas e respostas semelhantes.
Quando você souber o número de casas decimais do numerador e do denominador, uma maneira mais confiável é multiplicar esses números primeiro para que possam ser tratados como números inteiros e, em seguida, realizar a divisão inteira neles:
Portanto, no seu caso,
1//0.01
deve ser convertido primeiro para o1*100//(0.01*100)
qual é 100.Em casos mais extremos, você ainda pode obter resultados "inesperados". Pode ser necessário adicionar uma
round
chamada ao numerador e denominador antes de executar a divisão inteira:Mas, se se trata de trabalhar com decimais fixos (dinheiro, centavos), considere trabalhar com centavos como unidade , para que toda a aritmética possa ser feita como aritmética inteira e somente converter para / da unidade monetária principal (dólar) ao fazer E / S.
Ou, como alternativa, use uma biblioteca para decimais, como decimal , que:
fonte
O que você tem que levar em conta é que
//
é ofloor
operador e como tal, você deve primeiro pensar como se você tem igual probabilidade de cair em 100 como em 99 (*) (porque a operação será100 ± epsilon
comepsilon>0
desde que as chances de conseguir exatamente 100,00 ..0 são extremamente baixos.)Você pode ver o mesmo com um sinal de menos,
e você deve ser tão (des) surpreso.
Por outro lado,
int(-1/.01)
executa primeiro a divisão e depois aplicaint()
o número, que não é mínimo, mas um truncamento para 0 ! o que significa que, nesse caso,conseqüentemente,
O arredondamento, porém, forneceria o resultado esperado do SEU para esse operador, porque, novamente, o erro é pequeno para esses números.
(*) Não estou dizendo que a probabilidade é a mesma, apenas estou dizendo que, a priori, quando você realiza tal cálculo com aritmética flutuante, é uma estimativa do que você está obtendo.
fonte
Os números de ponto flutuante não podem representar exatamente a maioria dos números decimais, portanto, ao digitar um literal de ponto flutuante, você realmente obtém uma aproximação desse literal. A aproximação pode ser maior ou menor que o número digitado.
Você pode ver o valor exato de um número de ponto flutuante convertendo-o para decimal ou fração.
Podemos usar o tipo Fraction para encontrar o erro causado por nosso literal inexato.
Também podemos descobrir como são os números de ponto flutuante de precisão dupla granular em torno de 100 usando nextafter from numpy.
A partir disso, podemos supor que o número do ponto flutuante mais próximo
1/0.01000000000000000020816681711721685132943093776702880859375
seja de fato exatamente 100.A diferença entre
1//0.01
eint(1/0.01)
é o arredondamento. 1 // 0,01 arredonda o resultado exato para o próximo número inteiro em uma única etapa. Então, obtemos um resultado de 99.int (1 / 0,01), por outro lado, arredonda em dois estágios, primeiro arredonda o resultado para o número de ponto flutuante de precisão dupla mais próximo (que é exatamente 100), depois arredonda esse número de ponto flutuante para o próximo número inteiro (que é novamente exatamente 100).
fonte
int(0.9) == 0
eint(-0.9) == 0
Se você executar o seguinte
A saída será:
É assim que é representado internamente, então, arredondá-lo para baixo
//
dará99
fonte
Decimal(0.01)
você chegar tarde demais, o erro já aparecerá antes de você ligarDecimal
. Não tenho certeza de como essa é uma resposta para a pergunta ... Você deve primeiro calcular 0,01 precisoDecimal(1) / Decimal(100)
, como mostrei na minha resposta.