# 15. 浮點數運算：問題與限制¶

```0.3
```

```0.33
```

```0.333
```

```0.0001100110011001100110011001100110011001100110011...
```

```>>> 0.1
0.1000000000000000055511151231257827021181583404541015625
```

```>>> 1 / 10
0.1
```

```>>> format(math.pi, '.12g')  # give 12 significant digits
'3.14159265359'

>>> format(math.pi, '.2f')   # give 2 digits after the point
'3.14'

>>> repr(math.pi)
'3.141592653589793'
```

```>>> 0.1 + 0.1 + 0.1 == 0.3
False
```

```>>> round(0.1, 1) + round(0.1, 1) + round(0.1, 1) == round(0.3, 1)
False
```

```>>> math.isclose(0.1 + 0.1 + 0.1, 0.3)
True
```

```>>> round(math.pi, ndigits=2) == round(22 / 7, ndigits=2)
True
```

```>>> x = 3.14159
>>> x.as_integer_ratio()
(3537115888337719, 1125899906842624)
```

```>>> x == 3537115888337719 / 1125899906842624
True
```

`float.hex()` method 以十六進位（基數為 16）表示 float，一樣可以給出你的電腦所儲存的精準值：

```>>> x.hex()
'0x1.921f9f01b866ep+1'
```

```>>> x == float.fromhex('0x1.921f9f01b866ep+1')
True
```

```>>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 == 1.0
False
>>> sum([0.1] * 10) == 1.0
True
```

`math.fsum()` 更進一步將數值被加到運行總計中的總計時追蹤所有「失去的位數」，以便結果僅有一次捨入。這比 `sum()` 慢，但在不常見的情況下會更準確，在這種情況下，大量輸入大多相互抵消，最終的總和會接近於零：

```>>> arr = [-0.10430216751806065, -266310978.67179024, 143401161448607.16,
...        -143401161400469.7, 266262841.31058735, -0.003244936839808227]
>>> float(sum(map(Fraction, arr)))   # Exact summation with single rounding
8.042173697819788e-13
>>> math.fsum(arr)                   # Single rounding
8.042173697819788e-13
>>> sum(arr)                         # Multiple roundings in extended precision
8.042178034628478e-13
>>> total = 0.0
>>> for x in arr:
...     total += x                   # Multiple roundings in standard precision
...
>>> total                            # Straight addition has no correct digits!
-0.0051575902860057365
```

## 15.1. 表示法誤差 (Representation Error)¶

Representation error（表示法誤差）是指在真實情況中，有些（實際上是大多數）十進位小數並不能精準地以二進位（基數為 2）小數表示。這是 Python（或 Perl、C、C++、JAVA、Fortran 和其他許多）通常不會顯示你期望的精準十進位數字的主要原因。

```1 / 10 ~= J / (2**N)
```

```J ~= 2**N / 10
```

```>>> 2**52 <=  2**56 // 10  < 2**53
True
```

```>>> q, r = divmod(2**56, 10)
>>> r
6
```

```>>> q+1
7205759403792794
```

```7205759403792794 / 2 ** 56
```

```3602879701896397 / 2 ** 55
```

```>>> 0.1 * 2 ** 55
3602879701896397.0
```

```>>> 3602879701896397 * 10 ** 55 // 2 ** 55
1000000000000000055511151231257827021181583404541015625
```

```>>> format(0.1, '.17f')
'0.10000000000000001'
```

`fractions``decimal` 模組能使這些計算變得容易：

```>>> from decimal import Decimal
>>> from fractions import Fraction

>>> Fraction.from_float(0.1)
Fraction(3602879701896397, 36028797018963968)

>>> (0.1).as_integer_ratio()
(3602879701896397, 36028797018963968)

>>> Decimal.from_float(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')

>>> format(Decimal.from_float(0.1), '.17')
'0.10000000000000001'
```