Olá Bruna,
como que 0.1 + 0.1 + 0.1 pode ser diferente de 0.3?!
O que está descrito abaixo foi parcialmente copiado da página
Floating Point Arithmetic: Issues and Limitations
Essas questões são discutidas em disciplinas de Cálculo Numérico do MAP e na disciplina MAC0210 Laboratório de Métodos Numéricos.
Um computador é um instrumento e como qualquer instrumento tem sua precisão e suas limitações e blá-blá-blá.
Valores representados na base 10 (= decimal) usam os dígitos 0,1,...,9 (="range(10)").
Base 10 é o sistema de representação que usamos no dia a dia.
Por exemplo, utilizamos a representação 0.625 em ponto flutuante (=floating point) para expressarmos a fração
6 × 10-1 + 2 × 10-2 + 5 × 10-2
A fração 1/3 é representada na base 10 como a dízima periódica 0.3333....
Valores representados na base 2 (= binária) usam os dígitos binários (= bits) 0 e 1.
Valores em ponto flutuante são representados no computador na base 2.
Por exemplo, escrevemos a fração binária 0.101 para representar o valor
1 × 2-1 + 0 × 2-2 + 1 × 2-3
Assim, o valor representado pela fração decimal (=base 10) 0.625 é o mesmo da fração binária (=base 2) 0.101.
Python 3.5.2 (default, Sep 10 2016, 08:21:44)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1*2**-1 + 0*2**-2 + 1*2**-3
0.625
>>> 1/2 + 1/8
0.625
>>>
A seguir escreveremos (...)b para indicar que ... é a representação de um valor na base b. Como vimos, (0.625)10 == (0.101)2.
Um consequência do computador representar apenas frações binária é que muitas das frações decimais, que usamos no nosso dia a dia, não podem ser representadas exatamente. Por exemplo, a fração decimal (0.1)10 é uma dízima periódica na base 2.
Python 3.5.2 (default, Sep 10 2016, 08:21:44)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0*2**-1 + 0*2**-2 + 0*2**-3 + 1*2**-4
0.0625
>>> 0*2**-1 + 0*2**-2 + 0*2**-3 + 1*2**-4 + 1*2**-5
0.09375
>>> 0*2**-1 + 0*2**-2 + 0*2**-3 + 1*2**-4 + 1*2**-5 + 0*2**-6 + 0*2**-7 + 1*2**-8
0.09765625
>>> 0*2**-1 + 0*2**-2 + 0*2**-3 + 1*2**-4 + 1*2**-5 + 0*2**-6 + 0*2**-7 + 1*2**-8 + 1*2**-9
0.099609375
>>> 0*2**-1 + 0*2**-2 + 0*2**-3 + 1*2**-4 + 1*2**-5 + 0*2**-6 + 0*2**-7 + 1*2**-8 + 1*2**-9 + 0*2**-10 + 0*2**-11 + 1*2**-12* 1*2**-13
0.09960940480232239
>>> print("%.20f" %0.1)
0.10000000000000000555
Não importa quantos dígitos 0 e 1 utilizemos, a fração decimal (0.1)10 não pode ser representada exatamente como uma fração binária.
1/10 = (0.1)10 = (0.00011001100110011001100110011...)2
Parando a representação com qualquer número finito de bits, nós obtemos uma aproximação do bom e velho 1/10.
Em muitos computadores, valores da classe float são aproximados usando frações binárias com um numerado de 53 dígitos binários (= bits) e com um denominador que é potência de dois.
No caso de 1/10 temos a fração 3602879701896397 / 2 ** 55.
Python 3.5.2 (default, Sep 10 2016, 08:21:44)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x = 0.1
>>> x.as_integer_ratio()
(3602879701896397, 36028797018963968)
>>> 2**55
36028797018963968
>>>
3602879701896397 / 2**55 é próximo, mas não é exatamente 1/10.
É a vida.