這一節詳細說明 “0.1” 示例,教你怎樣自己去精確的分析此類案例。假設這里你已經對浮點數表示有基本的了解。
_Representation error_?提及事實上有些(實際是大多數)十進制小數不能精確的表示為二進制小數。這是 Python (或 Perl,C,C++,Java,Fortran 以及其它很多)語言往往不能按你期待的樣子顯示十進制數值的根本原因:
~~~
>>> 0.1 + 0.2
0.30000000000000004
~~~
這是為什么? 1/10 不能精確的表示為二進制小數。大多數今天的機器(2000年十一月)使用 IEEE-754 浮點數算法,大多數平臺上 Python 將浮點數映射為 IEEE-754 “雙精度浮點數”。754 雙精度包含 53 位精度,所以計算機努力將輸入的 0.1 轉為 J/2**N 最接近的二進制小數。_J_?是一個 53 位的整數。改寫:
1 / 10 ~= J / (2**N)
為:
J ~= 2**N / 10
J 重現時正是 53 位(是 >= 2**52 而非 < 2**53 ), N 的最佳值是 56:
~~~
>>> 2**52
4503599627370496
>>> 2**53
9007199254740992
>>> 2**56/10
7205759403792793
~~~
因此,56 是保持 J 精度的唯一 N 值。J 最好的近似值是整除的商:
~~~
>>> q, r = divmod(2**56, 10)
>>> r
6
~~~
因為余數大于 10 的一半,最好的近似是取上界:
~~~
>>> q+1
7205759403792794
~~~
因此在 754 雙精度中 1/10 最好的近似值是是 2**56,或:
7205759403792794 / 72057594037927936
要注意因為我們向上舍入,它其實比 1/10 稍大一點點。如果我們沒有向上舍入,它會比 1/10 稍小一點。但是沒辦法讓它 恰好 是 1/10!
所以計算機永遠也不 “知道” 1/10:它遇到上面這個小數,給出它所能得到的最佳的 754 雙精度實數:
~~~
>>> .1 * 2**55
7205759403792794.0
~~~
如果我們把這小數乘以 10**55,我們可以看到其55位十進制數的值:
~~~
>>> 3602879701896397 * 10 ** 55 // 2 ** 55
1000000000000000055511151231257827021181583404541015625
~~~
這表示存儲在計算機中的實際值近似等于十進制值 0.1000000000000000055511151231257827021181583404541015625。許多語言(包括舊版本的Python)會把結果舍入到17位有效數字,而不是顯示全部的十進制值:
~~~
>>> 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'
~~~
- Python 入門指南
- 1. 開胃菜
- 2. 使用 Python 解釋器
- 2.1. 調用 Python 解釋器
- 2.2. 解釋器及其環境
- 3. Python 簡介
- 3.1. 將 Python 當做計算器
- 3.2. 編程的第一步
- 4. 深入 Python 流程控制
- 4.1. if 語句
- 4.2. for 語句
- 4.3. range() 函數
- 4.4. break 和 continue 語句, 以及循環中的 else 子句
- 4.5. pass 語句
- 4.6. 定義函數
- 4.7. 深入 Python 函數定義
- 4.8. 插曲:編碼風格
- 5. 數據結構
- 5.1. 關于列表更多的內容
- 5.2. del 語句
- 5.3. 元組和序列
- 5.4. 集合
- 5.5. 字典
- 5.6. 循環技巧
- 5.7. 深入條件控制
- 5.8. 比較序列和其它類型
- 6. 模塊
- 6.1. 深入模塊
- 6.2. 標準模塊
- 6.3. dir() 函數
- 6.4. 包
- 7. 輸入和輸出
- 7.1. 格式化輸出
- 7.2. 文件讀寫
- 8. 錯誤和異常
- 8.1. 語法錯誤
- 8.2. 異常
- 8.3. 異常處理
- 8.4. 拋出異常
- 8.5. 用戶自定義異常
- 8.6. 定義清理行為
- 8.7. 預定義清理行為
- 9. 類
- 9.1. 術語相關
- 9.2. Python 作用域和命名空間
- 9.3. 初識類
- 9.4. 一些說明
- 9.5. 繼承
- 9.6. 私有變量
- 9.7. 補充
- 9.8. 異常也是類
- 9.9. 迭代器
- 9.10. 生成器
- 9.11. 生成器表達式
- 10. Python 標準庫概覽
- 10.1. 操作系統接口
- 10.2. 文件通配符
- 10.3. 命令行參數
- 10.4. 錯誤輸出重定向和程序終止
- 10.5. 字符串正則匹配
- 10.6. 數學
- 10.7. 互聯網訪問
- 10.8. 日期和時間
- 10.9. 數據壓縮
- 10.10. 性能度量
- 10.11. 質量控制
- 10.12. “瑞士軍刀”
- 11. 標準庫瀏覽 – Part II
- 11.1. 輸出格式
- 11.2. 模板
- 11.3. 使用二進制數據記錄布局
- 11.4. 多線程
- 11.5. 日志
- 11.6. 弱引用
- 11.7. 列表工具
- 11.8. 十進制浮點數算法
- 12. 接下來?
- 13. 交互式輸入行編輯歷史回溯
- 13.1. 行編輯
- 13.2. 歷史回溯
- 13.3. 快捷鍵綁定
- 13.4. 其它交互式解釋器
- 14. 浮點數算法:爭議和限制
- 14.1. 表達錯誤
- 15. 附錄
- 15.1. 交互模式