2019年2月15日 星期五

測試函數

測試函數


Ching-Tang Tseng
Hamilton, New Zealand
16 February 2019
ilikeforth@gmail.com
http://forthfornight.blogspot.com



我自建浮點運算系統、自建函數,建成之後,首先碰到的問題,就是會問自己建的到底對不對?

測試函數就成了自建系統後的必要工作,否則日後的應用會連自己也未必相信計算結果確實可靠。有許多人討論過測試方法,甚至於提供測試程式,但我鮮少利用,道理無它,那些資源,怎麼看都覺得薄弱不堪、有所欠缺。

我自覺比較可靠的測試方法,就是應該簡化成:直接取得一序列的函數值,直接繪圖顯示結果,目視就能判斷函數的功能正確與否。

不幸,我們通常使用的 FORTH 系統,發展的初始階段,通常不具有能夠順利繪圖的功能,因此,不易順利達到這樣的測試目的。

我在進入 64 位元環境後,全面改用 Linux 作業系統,使用 Lina64 作為解決問題的主要工具。取得的原始系統,除了沒有浮點運算的功能外,也沒有繪圖的功能,若要完成自己認定的打算,確實困難。

羅馬城不是一天建成的,我所使用的工具,更不是一到手就擁有一切。欠缺的功能,我慢慢地建,可用的資源,我慢慢地找、慢慢地試。整個系統沒有已經發展完成的一天,也沒有絕對完美的結果,並不是只有 Lina64 才是這樣,過去,我曾經用過的所有 FORTH 系統都是這樣。這是 FORTH 的本質,它恆在發展,沒有止境。

這次,我想完成以繪圖結果測試函數的程式。而且,我希望能夠辦到只執行一個 main 指令,系統就能完全自動達到目的。需要人工調整的工作,充其量,於測試不同函數時,可以只在程式中,重設測試上下限與函數名稱。 

這樣的要求,繪圖函數必須可由 FORTH 系統自己控制。也許需要透過一個數據檔案來傳遞大量函數值,因此就需要一套由 FORTH 自己設計出來的檔案操作程式。除此之外,FORTH 系統也要能夠將一序列的函數值自動存進檔案。 

這不是一般淺顯的程式設計問題,設計時,有一序列的規矩要遵循,能完成工作便是萬幸。

我從網上剪貼了單張常見的函數曲線圖,來源網頁已不可考,這張函數圖單純的只是用來供作自己體會函數圖形的樣式而已。希望在完成這份貼文時,能有我自己的發展成果。換句話說,程式我還沒有寫,但我心中早有腹案,我有自信能夠完成。





幾個測試成果:

























我自創的這套測試方式,由多個分開的檔案,分工做事,再總合於單一個檔案完成測試:

首先在數據產生程式 datagen.f 中,設定指定測試範圍與函數名稱。

無論測試範圍為多少,全部先行劃分成 1000 個點,然後計算出函數值,轉存入一個叫做 data.txt 的純文字數據檔案。

最後,將這個只有 1000 組數字的純文字數據檔案,交給繪圖程式 oneset.py 直接讀取使用,立即繪出圖形。

上述三個檔案的執行程序,可以在 Lina64 系統中,直接使用單一個叫做 main 的程式,總管所有的工作,一氣呵成,測試速度非常快,成果一目了然。

這些函數圖,都可以在程式中,先行修改、編寫出三項設定( 1. 在數據產生程式中給予指定測試範圍及 2. 測試函數名稱,3. 更換繪圖程式中的抬頭名稱。)後,只執行 fload main 載入程式,不用再作其他任何事情,一秒鐘內就完成顯示。

這樣的測試性能,除了表示 FORTH 程式語言的特質足夠優良外,更該感謝 Lina64 系統的設計者:Albert ven der Host,他建立了作業系統與 Lina64 之間的健全關係,否則,我不可能如此輕易地實現這樣的構想。

我在全程的測試過程中,曾經發現過許多問題,立刻回頭修正設計,拖了幾天,才在此貼出圖示成果。

回顧文首所述,別的測試方法,總令我覺得薄弱不堪、有所欠缺。這些顯示了設計得足夠健全的函數圖,是曾經歷過顯示不太健全後,回頭修改原始程式才完成的,這就是這種測試方法的妙處。不是所有的 FORTH 系統都具有這樣的性能,這也是為什麼目前我堅持只用 Lina64 FORTH 系統的主因。


展示完全正確的函數圖,不稀奇,也不能凸顯我所強調之測試重點的好處到底在那裡?

好處是透過這種技術,我就擁有常人不易獲得的可貴瑕疵對照圖。在這次發展這個技術的過程中,我獲得了許多這方面的寶貴資料,為數眾多,我單舉一例說明:


我在設計對數函數時,根據硬體數學運算處理器的基本工作原理,應該首先設計出以 2 為基底的對數函數 lb,它算是一個最基本的原生函數。隨後,以指數 e 為基底的對數函數 ln,及以 10 為基底的對數函數 log,才以次生函數的方式產生出來。

很不幸,我在進行測試時,顯示了下列這樣的函數圖。大部份函數值都是正確的,但出現了七個奇點,在這七個奇點的附近,函數值不正確,令我感到非常遺憾。




經過一番掙扎,不斷地驗證,耗了我八天的時間,我才再度確定原來所使用的演算法完全沒有問題。最後,只好強行在程式中插入了每次都將演算結果列印出來的審核程式,終於看到了出問題的地方,就只是最後一回的計算。於是,我確定了問題的根源,僅只是我自訂的演算極限最小值 epsilon 設定得不合理,原始的設定值是 1.0 * 10 ** -17 ,修正為 1.0 * 10 ** -16 後,所有的 lb 函數值就完全正確了。

一個 64 位元能表示的單整數是 19 位數:

-1 1 rshift .
9223372036854775807  OK
-1 1 rshift nlg .
19  OK

我憑此設計浮點數的四則運算時,必須犧牲一位數,也就是最精確值最多只能為 18 位數。有些函數於演算出函數值時,可能需要上百次的回算,系統中許多地方都要使用到的計算下限 epsilon 便不宜設定為太接近極限邊緣之負的 17 次方,必須再少一位數而為負的 16 次方。

數學運算處理器是使用 80 個位元來演算出結果,自然能比我只用 64 位元的演算結果更為精確幾位數。

我設計自己的浮點系統,其目的不在與硬體數學運算處理器的性能比高下,硬體製造廠商不會給我們詳細的演算技術資料,你只能跟著使用手冊中的說明,下命令來得到結果。

我若強行使用 128 位元來設計浮點系統,結果當然就能比硬體的好,但這不是我研發的目的,我的目的在要求自己完全能用純粹的軟體技術達到目的,我辦到了。

這樣的測試,是對自己設計之系統的最佳肯定方法,此後便能大方使用這套系統於數學計算方面的各種應用。我不想再浪費時間介紹自己的設計,我只想活用整套系統。因此,本網頁將暫停貼文,以便自己能有更多的時間研發我有興趣的問題。