Python 的 IEEE-754 雙精度浮點數在 F(78) = 8944394323791464 之後就無法精確表示費氏數列的值,而 F(79) 因為 2^53 是 64 位元浮點數能容納的最大整數,所以一開始就會被四捨五入。這個單一的精度界線會讓許多 Python 迴圈變成近似機器,即使原始碼看起來應該是正確的,這也解釋了為什麼用 print() 印出 fib(100) 的結果會跟你從教科書抄下來的值不一致。如果你需要從 F(0) 開始、可驗證且有索引的費氏數列,用於測試 fixture、程式碼比對或課堂範例,最乾淨的做法就是在 Python 之外,用精確算術一次性產生它們。費氏數列產生器在瀏覽器內使用 BigInt 整數執行遞迴關係式,將每一列格式化為 F(index) = 十進位值,並複製與畫面上看到的相同、以換行分隔的清單。因為加法是使用任意精度整數而非浮點數執行,所以像 F(100) = 354224848179261915075 這種值的每一個位數都會被完整保留,不會加上科學記號、四捨五入或千分位分隔符號。整個從 F(0) 到 F(999) 的數列都在本機執行,不需要上傳、帳號或 API 呼叫。

為什麼 Python 浮點算術在 F(78) 之後會失效
Python 內建的 int 型別是任意精度的,所以像 a, b = a + b, a 這種天真的迴圈,無論你要哪個索引,都能精確保留每一位數。只有當演算法使用 float、NumPy 的 float64,或任何涉及次方與開根號的閉合形式表示式時,精度斷崖才會出現。這三條路徑都會塌縮到 IEEE-754 雙精度,而每個 double 都共用同一個硬限制:超過 2^53 - 1 的整數無法精確表示。這個限制與費氏數列的值對齊得很尷尬。F(78) = 8944394323791464 還能表示,但 F(79) = 14472334024676221 已經超過最大安全整數,在儲存的瞬間就會被四捨五入。
四捨五入並不是一個表面的 bug。一旦某個值誤差了一個單位,下一次迭代就會加到錯誤的數字上,誤差也會累積。像 Binet 公式這種閉合形式方法,在四捨五入到最近整數之前會把 phi 升到 n 次方再除以根號五,也會面臨同樣的問題,只是四捨五入的方向不同。最安全的對策就是讓遴歸關係式維持在整數路徑上,並針對特定值與一個本身就精確的來源進行驗證。
產生器如何保留每一位數的精確性
費氏數列產生器在瀏覽器分頁內使用 BigInt 整數執行遞迴關係式。就這個目的而言,BigInt 是 JavaScript 對應於 Python int 的型別:它會視需要成長,並在轉換時印出每一個十進位數字。產生器從 (current = 0, next = 1) 開始配對,記錄當前索引,然後將配對推進到 (next, current + next) 以進行下一步。因為每次加法都是精確的,所以記錄下來的輸出在每個索引都是精確的。
每一列都以 F(index) = 十進位值 的形式寫成,沒有逗號、沒有底線,也沒有科學記號。輸出下方的摘要行永遠會回報兩個數字:產生了多少列,以及到達了哪個最終索引。因此,請求十個項目可以確認涵蓋 F(0) 到 F(9),而不是 F(0) 到 F(10)。1,000 項的上限是產品效能邊界,目的是讓渲染出來的文字保持易於管理。這不是費氏數列在數學上的限制,也與只需要較短區塊的 Python 工作流程無關。
如何使用工具產生精確且有索引的清單
當你想在不執行任何 Python 程式碼的情況下取得經過驗證的費氏數列值時,請使用以下工作流程:
- 在瀏覽器中開啟費氏數列產生器。頁面上的第一個欄位是 count 輸入框。
- 輸入一個介於 1 到 1,000 之間的整數 count。count 是你想要的列數,而不是最終索引;20 會產生 F(0) 到 F(19)。
- 產生數列並等待摘要行。摘要會回報產生了多少項,以及到達的最終索引。如果這兩個數字與你預期的不同,請修正 count,而不是相信一個被重新命名的輸出。
- 抽查輸出的開頭:F(0) = 0、F(1) = 1、F(2) = 1、F(3) = 2、F(4) = 3、F(5) = 5。這裡若有任何不一致,就有理由拒絕整個執行結果。
- 找到最後一列,並確認其索引與摘要中聲明的最終索引一致。請求 50 項必須以 F(49) 結束,而不是 F(50)。
- 使用複製控制項複製以換行分隔的清單。如果瀏覽器拒絕剪貼簿存取,小工具會說明這個限制;在這種情況下,請手動選取可見的文字並貼到你的編輯器中。
- 將結果貼到需要它的 Python 檔案、README 或測試 fixture 中。編輯 count 會清除舊的數列,這樣舊的清單就不會在新的、尚未處理的輸入下殘留在畫面上。
比較 Python 中用於費氏數列精度的數值型別
Python 中不同的數值型別會在不同索引達到精度極限。下表總結了每個常用型別開始失去精確數字的範圍。這些界限源自 IEEE-754 和二補數的限制,而不是執行遞迴關係式,並且與OEIS A000045中記錄的值一致。
| 數值型別 | 最大的精確費氏數列索引 | 底層常數 |
|---|---|---|
| Python int | 無上限 | 任意精度整數 |
| Python float (IEEE-754 雙精度) | F(78) = 8944394323791464 | 2^53 - 1,約 9.007 x 10^15 |
| NumPy int64 | F(92) = 7540113804746346429 | 2^63 - 1,約 9.223 x 10^18 |
| NumPy float64 | F(78) = 8944394323791464 | 2^53 - 1 |
請選擇符合你實際所需索引範圍的型別,或者對於超出上表的任何索引,使用任意精度路徑。
單一步驟的遞迴關係式演算
為了讓遞迴關係式更具體,請採用 OEIS A000045 中記錄的 F(77) 和 F(78) 值,僅透過加法來計算 F(79):
F(77) + F(78) = 5,527,939,700,884,757 + 8,944,394,323,791,464 = 14,472,334,024,676,221
這個結果就是 F(79) 的精確值。雙精度浮點數無法儲存它;四捨五入會根據方向強迫它變成 14,472,334,024,676,220 或 14,472,334,024,676,222。產生器的 BigInt 路徑會完整地寫出 14,472,334,024,676,221 的每一位數,這正是你的 Python 驗證程式碼需要用來進行斷言的形式。
為什麼以零為起始的定義很重要
有些參考資料會從 1, 1 開始顯示清單,這使得 F(1) 指的是第一個 1 而不是種子 0。產生器使用 NIST DLMF 和 OEIS A000045 所記錄的、以零為起始的定義,其中 F(0) = 0 且 F(1) = 1。對於每個大於等於二的索引 n,F(n) 等於 F(n-1) + F(n-2)。這個標記選擇並不是一個怪癖:它讓遞迴關係式的陳述保持一致,因為你可以寫成 F(2) = F(1) + F(0) = 1 + 0 = 1,而無需另外處理一個基礎案例。如果你的 Python 程式碼使用以一為起始的索引,產生器的索引看起來會偏移一個,而兩者之間的轉換純粹是索引欄位的算術運算,而不是猜測哪一列對應哪一項。
在 Python 專案中實際運用輸出
在真實的程式碼庫中,有三個具體的擺放位置會出現:
- 測試 fixture。把一整段有索引的值放進 conftest.py 或 JSON fixture,然後斷言你的遞迴或記憶化 fib() 會回傳相同索引的相同數列。這些 fixture 成為一項契約,可以及早捕捉到差一錯誤。
- 基於屬性的測試。Hypothesis 和類似的函式庫接受明確的範例加上不變式。將產生器的輸出與 fib(n) + fib(n + 1) == fib(n + 2) 這樣的屬性配對,屬性檢查器就能在 fixture 允許的範圍內深入取樣。
- 文件。將有索引的值內嵌在 README 中,這樣跟著步驟學習的人就能用眼睛驗證遴歸關係式,並對應到每一行的索引。以零為起始的標記消除了當某些參考資料從 1, 1 開始清單時出現的差一混淆。
對於超過 F(999) 的數列,請切換到專為大量資料設計的專屬程式設計環境。一千項的上限讓網頁工具的 DOM 和剪貼簿承載量保持夠小,即使包含 F(999) 也能保持反應靈敏,但這也是該工具唯一施加的限制。
產生器刻意不做的事
範圍界定讓輸出保持可預測。小工具會產生從 F(0) 到 F(n) 的有索引清單,除此之外什麼都不做。它不會檢查成員關係;「21 是費氏數嗎?」不是這個工具會回答的問題。它不會分解項、標記費氏質數、計算比例、繪製螺旋,或對數列中的自然或財務模式進行任何斷言。它也不會上傳輸入、儲存歷史記錄、呼叫遠端數列 API,或讓你登入。所有驗證、加法、格式化和複製的動作都在本機執行,產生的文字永遠不會離開瀏覽器。如果你的 Python 工作流程需要上述任何操作,請自行在工具給你的輸出上執行它們。
這如何與其他費氏數列任務搭配
同一個產生器也可以作為用任何語言(包括 Python)撰寫的程式碼的健全性檢查。如需更深入探討精確數字問題,以及其他測試向量和驗證模式,請參閱如何使用精確數字找出費氏數列指南。這兩篇文章涵蓋了部分重疊的內容,因為使用的是同一個工具,但這裡以精確度優先的切入點,是為了解決 Python 程式碼在嘗試使用一個無法表示所要求值的 float 時所出現的特定失敗模式。
如需更深入的探討,請參閱在 Java 中列印費氏數列:精確且有索引的項。