Pergunta

Como você explica a imprecisão do ponto flutuante a novos programadores e leigos que ainda acham que os computadores são infinitamente sábios e precisos?
Você tem um exemplo favorito ou anedota que parece ter a idéia muito melhor do que uma explicação precisa, mas seca,?
Como isso é ensinado nas aulas de ciência da computação?

Foi útil?

Solução

Existem basicamente duas grandes armadilhas que as pessoas tropeçam com números de ponto flutuante.

  1. O problema da escala. Cada número de FP possui um expoente que determina a "escala" geral do número para que você possa representar valores realmente pequenos ou realmente amplos, embora o número de dígitos que você possa dedicar a isso seja limitado. A adição de dois números de escala diferente às vezes resulta no menor que é "comido", pois não há como encaixá -lo na escala maior.

    PS> $a = 1; $b = 0.0000000000000000000000001
    PS> Write-Host a=$a b=$b
    a=1 b=1E-25
    PS> $a + $b
    1
    

    Como analogia para este caso, você pode imaginar uma grande piscina e uma colher de chá de água. Ambos são de tamanhos muito diferentes, mas individualmente você pode entender facilmente o quanto eles são aproximadamente. Derramar a colher de chá na piscina, no entanto, o deixará ainda com aproximadamente uma piscina cheia de água.

    (Se as pessoas que aprendem isso têm problemas com a notação exponencial, também se pode usar os valores 1 e 100000000000000000000 ou então.)

  2. Depois, há o problema da representação binária versus decimal. Um número como 0.1 Não pode ser representado exatamente com uma quantidade limitada de dígitos binários. Algumas línguas mascaram isso, no entanto:

    PS> "{0:N50}" -f 0.1
    0.10000000000000000000000000000000000000000000000000
    

    Mas você pode "amplificar" o erro de representação adicionando repetidamente os números:

    PS> $sum = 0; for ($i = 0; $i -lt 100; $i++) { $sum += 0.1 }; $sum
    9,99999999999998
    

    Não consigo pensar em uma boa analogia para explicar corretamente isso. É basicamente o mesmo problema por que você pode representar 1/3 Somente aproximadamente em decimal, porque para obter o valor exato, você precisa repetir os 3 indefinidamente no final da fração decimal.

    Da mesma forma, as frações binárias são boas para representar metades, quartos, oitavos, etc. Mas coisas como um décimo produzirão um fluxo infinitamente repetido de dígitos binários.

  3. Depois, há outro problema, embora a maioria das pessoas não tropeça nisso, a menos que elas estejam fazendo grandes quantidades de coisas numéricas. Mas então, esses já sabem sobre o problema. Como muitos números de ponto flutuante são apenas aproximações do valor exato, isso significa que, para uma determinada aproximação f de um número real r Pode haver infinitamente muitos outros números reais r1, r2, ... que mapeiam exatamente a mesma aproximação. Esses números estão em um certo intervalo. Vamos dizer isso rmin é o valor mínimo possível de r Isso resulta em f e rmáx o valor máximo possível de r para o qual isso se mantém, então você tem um intervalo [rmin, rmáx] onde qualquer número nesse intervalo pode ser o seu número real r.

    Agora, se você executar cálculos nesse número - adquirir, subtrair, multiplicar etc. - você perde precisão. Cada número é apenas uma aproximação, portanto você está realmente realizando cálculos com intervalos. O resultado também é um intervalo e o erro de aproximação só aumenta, aumentando assim o intervalo. Você pode recuperar um único número desse cálculo. Mas isso é apenas 1 número do intervalo de possível Resultados, levando em consideração a precisão dos seus operandos originais e a perda de precisão devido ao cálculo.

    Esse tipo de coisa é chamado Aritmético intervalado E pelo menos para mim, fazia parte do nosso curso de matemática na universidade.

Outras dicas

Mostre a eles que o sistema base-10 sofre de exatamente o mesmo problema.

Tente representar 1/3 como uma representação decimal na base 10. Você não poderá fazê -lo exatamente.

Portanto, se você escrever "0.3333", terá uma representação razoavelmente exata para muitos casos de uso.

Mas se você voltar a uma fração, obterá "3333/10000", que é não o mesmo que "1/3".

Outras frações, como 1/2, podem ser facilmente representadas por uma representação decimal finita na Base-10: "0,5"

Agora, base-2 e base-10 sofrem essencialmente o mesmo problema: ambos têm alguns números que não podem representar exatamente.

Embora a Base-10 não tenha nenhum problema em representar 1/10 como "0,1" na Base-2, você precisará de uma representação infinita começando com "0,000110011 ..".

Como é isso para um explante para o leigo. Uma maneira de os computadores representam números é contando unidades discretas. Estes são computadores digitais. Para números inteiros, aqueles sem parte fracionária, os computadores digitais modernos contam poderes de dois: 1, 2, 4, 8. ,,, valor de lugar, dígitos binários, blá, blá, blá. Para frações, os computadores digitais contam poderes inversos de dois: 1/2, 1/4, 1/8, ... o problema é que muitos números não podem ser representados por uma soma de um número finito desses poderes inversos. O uso de mais valores de local (mais bits) aumentará a precisão da representação desses números de 'problema', mas nunca o obtém exatamente porque ele possui apenas um número limitado de bits. Alguns números não podem ser representados com um número infinito de bits.

Soneca...

Ok, você deseja medir o volume de água em um recipiente e só tem 3 xícaras medidores: xícara cheia, meia xícara e um quarto de xícara. Depois de contar o último copo completo, digamos que haja um terço de uma xícara restante. No entanto, você não pode medir isso porque não preenche exatamente nenhuma combinação de copos disponíveis. Ele não preenche a meia xícara e o transbordamento da xícara é muito pequeno para preencher qualquer coisa. Então você tem um erro - a diferença entre 1/3 e 1/4. Este erro é composto quando você o combina com erros de outras medições.

Em Python:

>>> 1.0 / 10
0.10000000000000001

Explique como algumas frações não podem ser representadas com precisão em binário. Assim como algumas frações (como 1/3) não podem ser representadas precisamente na base 10.

Outro exemplo, em c

printf (" %.20f \n", 3.6);

incrivelmente dá

3.60000000000000008882

Aqui está o meu entendimento simples.

Problema: O valor 0,45 não pode ser representado com precisão por um bóia e é arredondado para 0,450000018. Por que é que?

Resposta: Um valor int de 45 é representado pelo valor binário 101101. Para tornar o valor 0,45, seria preciso se você pudesse levar 45 x 10^-2 (= 45 /10^2.) Mas isso é impossível porque Você deve usar a base 2 em vez de 10.

Portanto, o mais próximo de 10^2 = 100 seria 128 = 2^7. O número total de bits que você precisa é 9: 6 para o valor 45 (101101) + 3 bits para o valor 7 (111). Então o valor 45 x 2^-7 = 0,3515625. Agora você tem um sério problema de imprecisão. 0,3515625 não está quase perto de 0,45.

Como melhoramos essa imprecisão? Bem, poderíamos mudar o valor 45 e 7 para outra coisa.

Que tal 460 x 2^-10 = 0,44921875. Agora você está usando 9 bits para 460 e 4 bits para 10. Então está um pouco mais próximo, mas ainda não está tão perto. No entanto, se o seu valor inicial desejado fosse 0,44921875, você obteria uma correspondência exata sem aproximação.

Portanto, a fórmula para o seu valor seria x = a x 2^b. Onde A e B são valores inteiros positivos ou negativos. Obviamente, quanto mais altos os números podem ser maiores, sua precisão se tornaria, no entanto, como você sabe o número de bits para representar os valores A e B são limitados. Para o Float, você tem um número total de 32. O dobro tem 64 e decimal tem 128.

Uma peça de estranheza numérica pode ser observada se converter 999999999999999999 para um float e de volta a um double. O resultado é relatado como 10000000, embora esse valor esteja obviamente mais próximo de 9999999, e embora 9999999.4999999999 rodadas corretamente para 99999999.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top