# 15. 浮点数算法：争议和限制¶

```0.125
```

```0.001
```

```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'
```

```>>> .1 + .1 + .1 == .3
False
```

```>>> round(.1, 1) + round(.1, 1) + round(.1, 1) == round(.3, 1)
False
```

```>>> round(.1 + .1 + .1, 10) == round(.3, 10)
True
```

fractions 模块支持另外一种形式的运算，它实现的运算基于有理数（因此像1/3这样的数字可以精确地表示）。

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

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

float.hex() 方法以十六进制表示浮点数，给出的同样是计算机存储的精确值:

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

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

```>>> sum([0.1] * 10) == 1.0
False
>>> math.fsum([0.1] * 10) == 1.0
True
```

## 15.1. 表达错误¶

Representation error 提及事实上有些（实际是大多数）十进制小数不能精确的表示为二进制小数。这是 Python （或 Perl，C，C++，Java，Fortran 以及其它很多）语言往往不能按你期待的样子显示十进制数值的根本原因。

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

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

J 重现时正是 53 位（是 `>= 2**52` 而非 `< 2**53` ）， N 的最佳值是 56:

```>>> 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
```

```>>> .1 * 2**55
7205759403792794.0
```

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

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

fractionsdecimal 模块使得这些计算很简单:

```>>> 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'
```