Monday, February 15, 2016

假太陽

這則《假太陽》我是還在學時閱讀,那時啃完後覺得滿震撼的。故事是說人類最熟悉的天體,每天抬頭就可以看到的太陽,竟然是假的!原本真的那個太陽供應的能源只夠地球演化出猿猴,現在這個「假」太陽是被「人」調包過的!

這則《假太陽》故事有點宗教味道,但又不脫(倪匡筆下)科幻橋段,初次閱讀的應該不少人會跟我一樣,整個腦袋瓜轟轟的吧 XD

GodspeedLee 的分享下得知《時間迴旋》一書也有假太陽的設定。趁年假,花了一天的時間,順手把《時間迴旋》啃完了。

就科幻的部分,《時間迴旋》描寫更細膩,它除了會說明太陽跟地球的演化史外,也針對太陽被替換的方式,提出一系列合理的影響。例如說,夜空沒了星斗,月亮也不見了,甚至極光也消失了。這些是很好的科幻素材,依附在上面的人事物也由此帶出。

不過也因為內容描述太過詳盡,加上敘事方式不是很連貫,所以我是覺得有點囉唆。不符合我啃書喜歡挑重點的習慣 :p

Wednesday, February 03, 2016

Embedded Systems 中的 Resource 管理

開發 Embedded Systems 常常會碰到 f/w 要用到圖、字型、甚至音樂等 resource 的情況。實務上一種處理方式是將這些 resource 轉換後串成一個大的 C array 。然後再用 resource ID 去讀取它們。一些書上稱這種轉換的工具叫 Resource Maker 。不過我不會採取這種作法,雖然還是透過 resource ID 讀取,但不把 resource 轉成 C array ,而是直接轉成 binary 格式,要用時才去 storage 裡 load 進來,因為這樣比較節省記憶體空間。

多年前我就用 Python 寫了類似的工具,那時我把這支程式稱作 weave 。後來慢慢精練,演變成今天要介紹的 ResourceLink

同樣地,這裡我主要也是著重在 ResourceLink 裡用到的領域專用語言,來看看我怎麼描述這些 resource files ,以下是個 res.lst 的例子:

:0x00       # start offset (default is 0x00)

:kind=A     # kind A for the enumeration
bat.png     # file size: 2877 bytes

:kind=B     # kind B for the enumeration
:4096       # offset to address: 4096
broom.png   # file size: 3083 bytes
candle.png  # file size: 2771 bytes
  • 這個文字檔列出了 bat.png, broom.png, candle.png 等三張圖檔檔名,當作 resource
  • # 一直到行尾代表註解。
  • : 接數字代表接下來的檔案要放的位置,單位為 bytes
  • :kind=name 用來對產生的 enumerator 作分類。
    • 除了用來區分 resource 的 type 外,我還常利用這個機制來處理多國語言。

把這個 res.lst ,餵給 ResourceLink 後,可以產生 ResID.h, ResMap.i, 跟 res.bin 。

res.bin 就是這些 resource 檔案(三張圖檔)連結成的單一檔案。

ResID.h 為每一個 resource 都指定一個 enumerator ID:

// Generated by the Resource Link v1.14
//    !author: Jiang Yu-Kuan 
//    !trail: reslnk.py id -oResID.h res.lst
#ifndef _RES_ID_H
#define _RES_ID_H


/** IDs of Resources */
typedef enum {
    RES_A_BEGIN,
    RES_PNG_bat = RES_A_BEGIN,
    RES_A_END,

    RES_B_BEGIN = RES_A_END,
    RES_PNG_broom = RES_B_BEGIN,
    RES_PNG_candle,
    RES_B_END,

    RES_End = RES_B_END,
    RES_Total = RES_End
} ResID;


#endif // _RES_ID_H

ResMap.i 是用來描述每個資源檔的偏移植跟大小的:

// Generated by the Resource Link v1.14
//    !author: Jiang Yu-Kuan 
//    !trail: reslnk.py map -dres -oResMap.i -a4 res.lst

//   offset,       size     (in bytes)
{         0,       2877},   // RES_PNG_bat (bat.png)
{      4096,       3083},   // RES_PNG_broom (broom.png)
{      7180,       2771},   // RES_PNG_candle (candle.png)

Friday, January 29, 2016

利用 LangConvert 工具處理多國語言

dic.xls

開發 Embedded Systems 相關應用時,常得處理多國語言。而系統資源受限的的場合,就算掛了 OS,往往也沒內建多國語言。這時候只能捲起袖子自己處理了。自幹的過程,很直覺地,多數人都會想到要有個類似右圖這樣的 Excel 字典檔當作翻譯表。

有了翻譯表後,我們還要有個字庫(font)。為了存取字庫裡的字,我們要先決定字序(character order)。有了字序後,我們就能根據字序,把翻譯表裡面的多國語言訊息,一一轉換成字序的串列(a sequence of character orders)。要秀某個訊息時,就根據這個字序列,回過頭把字庫裡的字形(glyph)抽取出來顯示。

如果要通吃幾乎各國的語言,一個奢侈的做法是直接用 Unicode 當字序,建立完整的字庫。不過這不適合用在資源受限的場合。

多年前,我遇到決定字序的問題時,想了一種簡單又好用的表示法。以 ASCII code 有對應字形的部分為例子,可以利用這個表示法描述如下:

# Printable characters of ASCII code

:0x20
 !"#$%&'()*+,-./0123456789:;<=>?
@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
`abcdefghijklmnopqrstuvwxyz{|}~
  • # 開頭的行表示該行是註解。
  • :0x20這行,代表接下來的字元從 0x20 (有可以寫成十進位的 32)開始編號。
  • 行尾的空白字元會被忽略掉。
  • 這整段看起來很直覺,就是列出了 ASCII code 32 到 127 間的字元。

因為英文太常用了,所以 32 到 127 這段通常會保留給 ASCII code 用。其他國家的語言,我們可以只列出有用到的,以節省字庫佔的儲存空間,以下是實際的例子:

# A sample character list file

:0x20
 !"#$%&'()*+,-./0123456789:;<=>?
@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
`abcdefghijklmnopqrstuvwxyz{|}~

:0xA0
áíñóúąćęśП
РУЭабвдежз
ийклмнопрс
туфщыьэюя中
件像册删单图子定客影
文时显检此电相示置菜
言设访语间除验간겠국
까뉴니메문미방범삭설
스습시앨어언영을이인
일자전정제지틸파표하
한화확?

有了翻譯表(dic.xls)跟字序表(char.lst)後,就可以利用 LangConvert 產生下述的幾個 C 原始程式:

  • LangID.h: Language ID 的 enum 。
  • MsgID.h: Message ID 的 enum。
  • mlang.i: 將每個訊息的多個語言版本,一一列出對應的字序串列

接下來還缺個從字序表轉出字形(glyph)的工具,這個大家可以自己練習看看。我這裡著重在介紹領域專用語言來表達字序表。

LangConvert 還有個新增的 command 可以透過 Google Translate 來對空的字典檔實施自動翻譯,有興趣的可以直接執行 demo_trans.bat 觀察看看。它會吃進還沒翻譯好的 dic_empty.xls ,然後吐出翻譯好的 dic_trans.xls 。

Sunday, January 24, 2016

利用 PicCrop 工具來切圖

iPod Touch 5g

記得小學製作海報時,會用剪貼的方式分工,快速拼湊出一張教室海報。時代進步了,現在大家都用電腦,我好幾次觀察到現在美術人員幫忙設計 UI 時,也常常會先把整體畫出來,然後再一塊塊的剪下來。這些剪下來的圖,還有個貼切的稱呼,叫「切圖」

現在有很多現成的切圖工具,幾乎都搭配 Photoshop 使用,甚至 Phothoshop 本身對這道工序也提供一定的支援。不過我沒打算在這介紹這些搭配 Photoshop 的圖形化工具,而是想設計一個專用的語言,來執行這個切圖的動作。

這個語言要告知原始圖檔,然後再列出每張被切下來的圖的位置、大小、甚至名字等。舉個例子,假設我要把圖中八個紅線匡起來的部分,一一切下來存檔。


最簡單的描述方式,大概就長這樣子:

# source picture
#---------------
ipod-touch-5th-black.png

# x,   y,  w,  h, target picture
#------------------------------------
117, 139, 62, 62, ico_FaceTime.png
190, 139, 62, 62, ico_Calendar.png
263, 139, 62, 62, ico_Photos.png
336, 139, 62, 62, ico_Camera.png
117, 223, 62, 62, ico_Weather.png
190, 223, 62, 62, ico_Clock.png
263, 223, 62, 62, ico_Maps.png
336, 223, 62, 62, ico_Videos.png
依照慣例, # 開頭的代表該行是註解。

下面八張小圖就是剪下來的切圖:



這個工具可以一次對多張的原始圖寫好要怎麼切的描述,切之前還可以反覆用紅線匡起來確認大小跟位置沒錯,然後只要 "One Touch" 就一口氣全切出來了。

照慣例,程式是用 Python 寫的,細部的安裝跟使用說明可以參考PicCrop 的 Overview

Sunday, January 17, 2016

以 EnumLookup 查詢 C enumerator

前陣子我把 AWK 撿回來練習,順便研究一下 Windows 下怎麼跑 GAWK 跟 AWKA 之類的工具。這期間,我想了幾個簡單的題目來練習。其中有個我稱作 EnumLookup 的工具,一些慣 C 的人應該會用到,所以在這裡做個分享。

顧名思義,這個工具的目的是針對 enum ,它可以在多個 C enumerations 上查詢 enumerator 或它們對應的值。這裡是安裝跟執行說明文件,大家可以根據上面的指引,直接下載內含執行檔的下載包來試用。

這個工具有個搭配的 enum.bat ,執行後可以雙向查詢:可反覆敲進數字來查對應的名字(enumerator),或者敲入名字(enumerator)來查對應的索引數字。

需要注意的是,這個工具沒有真的去實作完整的 C enum parser ,只是認「通常」情況下的 enum 特徵。下面這種寫成一行的寫法,無法正確執行:

typedef enum {SPRING, SUMMER, AUTUMN, WINTER} Season;

因為這工具不認得寫成一行的寫法,只認得下面這種,比較正規的,分行寫法:

typedef enum {
    SEASON_BEGIN,
    SPRING = SEASON_BEGIN, 
    SUMMER, 
    AUTUMN, 
    WINTER,
    SEASON_END,
    SEASON_TOTALS = SEASON_END // the total number of seasons
} Season;


Sunday, February 01, 2009

The Menu Show

Base Maps of Menus

接連多日的年假已接近尾聲,吃吃喝喝之餘,很自然地就想到一個跟吃喝有關的練習。雖然年假前在公司搞的相框產品確實用到各式 UI 選單(menu),但我在這裡要聊的是名副其實的菜單(menu)。

為了製作精美的菜單,我用 Google 搜來幾張食物的圖片準備用作底圖,除了一張用作食物主選單底圖外,其餘三張分別用作飲料類水果類蔬菜類等用途。

考慮到要製作的菜單不只一張,且每張菜單的內容會一直修改,所以我不打算用繪圖軟體繪製菜單,這個重任當然要照慣例,委託給爬說語。要執行這支程式,必須先以 YAML 語法,利用文字編輯器寫下菜單的內容及呈現方式,存成 menu.yaml 。程式執行時會自動讀進這個描述檔,然後描繪出期望的菜單來。例如說,有張菜單長成這樣:

menu of drinks

這是一張飲料類的菜單,它有 Coffee, Juice, Soda Water, Tea 等選項,要產生這張菜單, menu.yaml 這個純文字檔就要列出飲料類菜單的內容:

title: '==   Drink   =='
items:
    - Coffee
    - Juice
    - Soda Water
    - Tea

除了填寫菜單的內容外,還要填寫菜單的呈現方式:

style:  # Style of title, items, focused items
    font: [cour.ttf, 24]
    title: {color: clYellow}
    items: {color: clWhite}

當然不能忘記告知從 Google 那搜來的底圖:

basemap:
    dim: [320, 240]  # dimensions of width and height
    color: clWhite
    image: drink.jpg
    type: image  # {color, image}

完整描述請參閱 menu.yaml 。程式請參閱 MenuShow.py

Tags: [] [] [] []

Sunday, July 13, 2008

Commands of the NAND

Applications of NAND

到電子商場逛一圈就會發現一堆產品都有 NAND flash 的身影(例如大拇哥,記憶卡,MP3 player,數位相框,甚至 PC 等)。前陣子和 simayi 閒聊時,他就提到:既然大家都愛用 NAND flash ,要是有人為它搞個 IP 或函式庫之類的,勢必可大幅節省開發時間。

相信處理過 NAND flash 的 firmware 人員,在啃讀 datasheet 的過程,難免得謹慎地交叉比對,好好推敲那也佔了不少篇幅的時序圖,以免自己還是不夠小心,誤解文意……

不知道大家看了那一疊 waveform 後,有什麼感想?我的看法是,那疊圖雖補足了許多重要細節,卻沒能好好強調重點,抽象度不夠。這根本是在折磨 firmware 人員,使我們構思演算法時綁手綁腳。

因此,在 K 完文件後,我為這疊 command waveforms 作的第一件事就是--提昇抽象度,強調重點,用 Computer Science 學生都看得懂的語言重新詮釋過:

Command Sequences (2k)

Command Sequences (2k, cont.)

基本的正規表示法就捕捉到 NAND flash commands 的內涵,充分表現出蘊藏其中的模式 :)

適當的表示法可以流暢表達所思所想,幫助我們釐清思緒、避免錯誤;好的表示法更可以提供新的洞見,讓我們解決乍看之下非常困難的問題,甚至進一步導致新的發現。

將來要實現自動化,或 Workflow 軟體時,正規、簡明的表示法更是一個必要的基礎設施。

後記:就 NAND flash 而言,僅僅是 command 的正規化還不夠方便;為 NAND flash 設計演算法時,最好再架上另層一抽象,以幫助思考。

Monday, June 30, 2008

NAND Flash 簡介

The insides of a SD Card

撬開一張 SD 卡,裡面最顯眼的,當然就是那大大一顆的 NAND flash ,我們餵給 SD 卡的資料都儲存在裡面;在 NAND flash 旁邊,還可看到一顆小一號的,那就是 controller IC , 要確保資料的儲存是安全可靠的,有九成的責任都要算在 controller 身上。

在硬體介面方面, NAND Flash 雖有 bus 結構,卻沒去區分 address bus 及 data bus 。在 NAND Flash 上進行任何操作(如 read, write, erase 等),都要透過 command ,且無論 address, data, 或 command,都以同一組 I/O bus 傳輸。

此外, NAND flash 在資料 program 或保存過程,還會隨機出錯,所以廠商才會建議搭配 ECC (Error Correcting Coding) ,以資料冗餘來偵測及更正這種隨機的錯誤。

Compare to NOR Flash

就算不考慮上述這些, NAND flash 還是非常難纏,對 firmware 人員來說更是如此:它無法像 RAM 或 ROM 那樣,隨插隨用;也不像 serial NOR flash 那樣,照著 spec. 下下 command 就能了事。我就以 Samsung K9F1G08U0B 這顆 NAND flash 為例,摘要如下:

Samsung K9F1G08U0B

相較於其他 floating gate 製品,NAND Flash 最讓人詬病的是壞塊(bad blocks),不但一出廠就允許壞塊存在,在保固的使用壽命內好塊還會陸續變成壞塊,更慘的是壞塊的發生還是隨機的。也因如此, NAND flash 的 firmware 或專用的檔案系統都得好好管理壞塊(Bad Block Management),讓用戶察覺不到壞塊存在。

再者,同樣是 floating gate 組出來的, NAND Flash 當然也有明顯的寫入次數限制,再加上它常用於頻繁、不均勻的寫入場合,所以要有一個叫作 wear leveling 的抽象層,讓針對單一邏輯位址的多次寫入,分散到不同的實體位址,以避免太快壽終。

The Algorithm

Tags: [] [] []

Monday, June 16, 2008

The Floating Gate

Floating-gate transistor

浮動閘(floating gate)一詞會讓我銘記於心,是因為閱讀了《矽眼》,該書提到以浮動閘紀錄類神經元突觸加權值,這是「類比」儲存的一個應用。

然而,對多數內嵌系統設計人員來說,浮動閘是用在「數位」儲存的,諸如 EPROM, EEPROM, NOR flash, NAND flash 等。

無論是 EPROM, EEPROM 或 NOR flash ,早先都是設計來在上面直接跑程式的(不用 copy 到 RAM 上跑,術語叫做 execute in place, XIP),所以有獨立的 data bus 及 address bus 。

為了省空間,後來很多 MCU 都把 EPROM, EEPROM 或 NOR flash 包進同一棵 chip 了,這造成外部的 EEPROM 或 Flash 開始走 serial 路線。 serial EEPROM 或 serial Flash 對外的 pin 腳雖然大幅精簡了,卻失去了 XIP 的特性,因此主要被拿來儲存程式之外的東西。

由於 EEPROM 是 byte-wise writable 的,用起來超方便,所以資料量不大時我們都會用它。例如一些儀表類的內嵌系統,諸如血壓計之類的,其內部的關鍵零件是感測器,這些感測器的元件特性會隨溫度等外在因素漂移,所以需要在事後作校正,這些校正用的參數就很適合用 serial EEPROM 儲存。

有時候我們要存的不僅僅是簡短的幾筆,而是長時間累積下來的一大串 log 。例如未來的病患可能會長時間配戴血壓計血糖計之類的,隨時紀錄血壓血糖變化,然後隔固定時間,自動將這些資料傳到醫院的監控中心。這種場合因為要存的資料量較大,也許該考慮採用 serial Flash 。

操作 serial (NOR) Flash 跟操作 serial EEPROM 類似,都有 OP-Code Phase, Address Phase, Data Phase 等三個階段。偏偏 (NOR) flash 只能 block-wise writable ,這意味著就算只想改變一個 block 的某幾個 bytes 的內容,還是得先把整個 block 的值都讀出來,在 RAM 中修改後,再寫回去。

這幾年正火熱的 NAND Flash 一開始就是設計來替代硬碟這類大容量的儲存媒體。它雖價廉、肚大,卻是個極不穩定的傢伙,不但允許出廠就有壞塊(bad block),使用過程壞塊還會隨機增加,更扯的是廠商還信誓旦旦說這是良品,要大家看著辦。良品都這副德行了,真不敢想像一些 down grade 的會長成什麼樣子 :p

文末,再附上簡短的比較:

  • (parallel) EEPROM
    • Byte-writable, i.e. byte-erasable and byte-programmable
    • Execute in place
    • Write special commands to unlock, erase or write
  • (parallel) NOR Flash
    • Erasing and writing must be on block-by-block basis
    • Execute in place
    • Write special commands to unlock, erase or write
  • serial EEPROM
    • EEPROM with serial interface
    • 3 phase commands for operations, e.g. read, write, and erase
    • Wear leveling is required
  • serial (NOR) Flash
    • NOR Flash with serial interface
    • 3 phase commands for operations, e.g. read, write, and erase
    • Page for programming; block for erasure
    • Wear leveling is required
  • NAND Flash
    • As a hard disk replacer
    • Use a NAND flash specific interface
    • Page for programming; block for erasure
    • Wear leveling is required
    • Bad block management is must
Tags: [] [] [] []

Saturday, June 07, 2008

The Analog Clock

Collage of the Digital Photo Frame

……秒針急急忙忙的去撥動每一根短棒,使它們產生意義。然後分針慢吞吞的做同樣的事,使那些短棒產生另一種意義。三種針的位置和關係不斷變更,在錶面上切割出許多角來,夾住那不可捉摸的時間。……(摘自作文七巧:P86)

算一算日子,在現任公司混吃也有九個月了。很幸運的,一進來就參與一顆 ASIC 的開發,從一開始的寫 tools 測試 FPGA 功能,後來的寫 f/w 測試 ASIC ,到最後的參與產品開發。照規劃,一開始只打算拿來秀秀圖,偶爾也秀秀時間日期。後來為了把這顆小 MCU 的能耐完全壓榨出來,前些日子我還幫它加了類比鐘(Analog Clock)。自此,相框就不再僅僅只是相框了:

The Analog Clocks

想起專科的畢業專題,我實作過一組函式庫,用來執行 3D 投影及相關的座標轉換。一晃眼已經十多年了,最近為了完成的這個類比鐘,竟然連描點畫線的程式都得自己手寫……

這也沒辦法,先前那支專題程式是在 16 bit 的 286 CPU 上跑的,硬體上有浮點運算器可用,軟體方面也有 DOS 版的 Borland C++ 提供的 BGI 繪圖函式庫。而這次的類比鐘卻要運作於 8 bit 的 8051 MCU。

首先,我寫了常用於光柵繪圖的點(pixel)運算及畫線程式。既然可以描繪出線段了,接著就一段一段的,拿來描出時鐘刻度及各式指針:

The Analog Clocks

由於底層光柵繪圖的座標系統(直角座標)實在不適合拿來描繪鐘面及指針,所以我只好祭出極座標。

雖然極座標到直角座標的轉換式是長成這樣的:

x = x0 + r⋅cos(θ)
y = y0 + r⋅sin(θ)

我實際採用的轉換式子卻是:

x = x0 + r⋅sin(minute)
y = y0 - r⋅cos(minute)

這是因為

  1. 數學上慣用的極座標以逆時針方向為正,順時針方向為負,把 sin 跟 cos 互換後,角度的正負才符合時針的轉向。
  2. 螢幕的 y 座標是往下長的,所以補個負號,讓它迷途知返。
  3. 最後,時鐘的角度單位只要有「一分鐘轉角」的精度就夠了。

值得一提的是,整個換算過程只用到整數運算,因為我不但採用了定點數運算技巧,還以爬說語產生了正弦、餘弦表格:

>>> from numpy import *
>>> scale = 127
>>> array([round(x) for x in scale*sin(arange(0, 2*pi, 2*pi/60))], dtype=int)
array([   0,   13,   26,   39,   52,   63,   75,   85,   94,  103,  110,
        116,  121,  124,  126,  127,  126,  124,  121,  116,  110,  103,
         94,   85,   75,   64,   52,   39,   26,   13,    0,  -13,  -26,
        -39,  -52,  -63,  -75,  -85,  -94, -103, -110, -116, -121, -124,
       -126, -127, -126, -124, -121, -116, -110, -103,  -94,  -85,  -75,
        -64,  -52,  -39,  -26,  -13])

>>> array([round(x) for x in scale*cos(arange(0, 2*pi, 2*pi/60))], dtype=int)
array([ 127,  126,  124,  121,  116,  110,  103,   94,   85,   75,   64,
         52,   39,   26,   13,    0,  -13,  -26,  -39,  -52,  -63,  -75,
        -85,  -94, -103, -110, -116, -121, -124, -126, -127, -126, -124,
       -121, -116, -110, -103,  -94,  -85,  -75,  -64,  -52,  -39,  -26,
        -13,    0,   13,   26,   39,   52,   63,   75,   85,   94,  103,
        110,  116,  121,  124,  126])
Tags: [] [] [] []

Monday, May 19, 2008

The Fraction from a Decimal

定點數運算常用於 embedded systems 中,因為大部分低階的 MCU (例如: 8051, PIC, AVR 等)開發環境雖提供浮點運算,卻是軟體模擬的,除了慢,還明顯佔用原本就少得可憐的記憶體空間。 C/C++ 語言雖無定點數運算專用語法,程式員卻可通過手動調整,有效以整數運算完成相同效果。

定點數運作的原理,簡言之,就是將原來的實數(real number)或者小數(decimal),改寫成分數(fraction):如果 x 是個含小數的實數,我們可以找來兩個整數(p, q),將它們相除,來近似原來的 x (p/q ~= x)。

實務上人們可能還會要求上述的 q 要是 2 的冪次,因為電腦處理的都是 0, 1 的二進位運算, q 表示成 2 的冪次可以達到較高的精度;另一個原因我想是許多 f/w 程式員都患了 shift 偏執症 :p

有個友人前陣子寫了江湖一點訣,還在 CSZone 引發一陣討論,有人提到說他用工程計算機把十進位數轉成二進位就搞定了,何苦寫程式來 try 出 p, q 。

最近興起,也想把數字轉成二進位以找出 p, q 時,發現手邊沒有合用的計算機,不爽之餘,再次以爬說語搞定這件事:

"""
File: fract.py
Converts a decimal into an equivalent fraction.
"""
__author__ = "Jiang Yu-Kuan, yukuan.jiang(at)gmail.com"
__date__ = "2008/05/17"
__revision__ = "1.2"

def fract(x, bits=8, deduced=False):
    """Find p, q such that p/q ~= x and p, q < 2**bits
    """
    p, q = x, 1
    while p < 2**bits:
        if deduced:
            print "%f = %d/%d" % (round(p)/float(q), round(p), q)
        p *= 2
        q *= 2
    return int(p/2+0.5), q/2

if __name__ == "__main__":
    p, q = fract(3.1415926535, deduced=True)
    print "p=%d, q=%d" % (p, q)

用例:

J:\trial\python>python -i fract.py
3.000000 = 6/2
3.250000 = 13/4
3.125000 = 25/8
3.125000 = 50/16
3.156250 = 101/32
3.140625 = 201/64
p=201, q=64
>>> from math import e, pi
>>> e
2.7182818284590451
>>> fract(e)
(174, 64)
>>> 174/64.
2.71875
>>> pi
3.1415926535897931
>>> fract(pi)
(201, 64)
>>> 201/64.
3.140625
>>>
Tags: [] [] []