2026年7月16日 星期四

自創浮點系統的新問題

自創浮點系統的新問題


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
16 July 2026


1. 問題的發現:

我在修改2025年11月2日貼文『­誤差函數的應用』中之實際程式,以供自創浮點系統使用時,出現了問題,顯示原始程式無法順利通過編譯。

2. 問題說明:

自創浮點系統的規格,係2024年7月10日貼文『設計浮點系統』文中的格式,這個格式於 2025 年我回台灣與 FORTH 同好開研討會時,當場重建在 gForth 系統過,性能驗證無誤。因此,修改『數據分析應用程式』時,應該是只須要將所有輸入的浮點數字改成由三個單元形成的浮點數字便可。很不幸,未能通過編譯。

3. 除錯記錄:

經過操作 Forth 傳統的除錯方式後,發現未能通過的設計只存在於最後一個 MAIN 指令中,而且就是列號為 60 的程式,原程式轉錄如下:

60 LET { erfx = ( ABS ( DATA ( I ) - AVG ) ) / ( sqrt ( 2 ) * sigma ) }

特別說明 ! 上式本為只能在浮點系統中的寫法,開平方函數操作的對象 2 能夠這樣寫,係我專為 Win32Forth 系統設計 ABC Forth 時的特例,系統編譯時,能自動將所有的數字都自動處理成浮點數,但不處理陣列、矩陣、張量的整數指標。至於在其他 Forth 系統中加建的 ABC Forth 則均不具有這種功能。

除錯後第一次的改寫,寫成下式,很不幸,仍然不能通過編譯。

60 LET { erfx = ( ABS ( DATA ( I ) - AVG ) ) / ( sqrt ( 2.0 E 0 ) * sigma ) }

磨耗了許久,我才確定,問題的根源就是這種自創浮點系統的數字格式,會搞混數學運算先後秩序的編譯,於是再增加了一層小括號,令 ( sqrt ( 2.0 E 0 ) ) 可以被強制優先執行,最終落定的格式寫法如下:

60 LET { erfx = ( ABS ( DATA ( I ) - AVG ) ) / ( ( sqrt ( 2.0 E 0 ) ) * sigma ) }

4. 檢討分析:

顯然,我自創的浮點系統,確實有未能全面顧及所有狀況的缺陷,透過原始 Lina64 Forth 系統中的 SEE 輔助除錯指令,可以看到能被編譯成功的設計內容。單獨重寫一列小程式來檢驗編譯成果時,能夠發現以三元浮點規格輸入的數字,如果運算優先度處理不當,編譯後會出現 0 * sigma 結合成一組運算的現象,執行 MAIN 指令時就顯示浮點運算無法執行的提示。

5. 解決方法:

暫時先使用能強迫可以被最優先執行的成對小括號 ( ..... ) 來解決問題。

現在只能預想,我使用三元浮點數輸入法中的 E 指令,也必需宣告它被執行時的優先度,系統才能被正確的執行。

6. 題外話:

設計系統、使用系統、測試系統是一件冗長辛苦的工作,能夠發現問題的初衷,係我想將一些有用的程式重新改寫進最新設計出來的系統中使用。能發現新問題是好事,不要氣餒,必須直接面對問題、解決問題。

挑選這個程式來修改,也有原因。我向來擅長親自動手做實驗,也擅長對取得的實驗數據進行技術分析。數據分析程式的功能很有用,它不但能應用於實際科學,也能實際應用於日常生活。

原始程式中舉例的應用,係我在從事於鋁合金冶煉時的測試數據。我也曾將此技術應用於微弱中子源的測量讀數分析中,效果非凡,曾經用它來過濾掉幾根無效的中子探測器,並從中挑選出僅存的一根略為多加 100V 工作電壓(正常為 2400V ,勉強改用 2500V) 尚能使用的中子探測器。從事於固態燃料電池研究時,也用此技術來分析被測量材料導電度的實驗數據。用得最多的地方,則是我在從事於原子爐運轉擔任工程師時,核驗技術員抄表所得數據的合理與否?當然,這樣做容易引起當事人的不滿,所以,我通常都是默默地做,除非數據相當關鍵,影響重大,我才會取用這種有統計根據的分析結果,公開要求當事人慎重處理抄表所得。我記得有位學弟在測量中子的通量分佈時,出現一個被照射金片的測量讀數超出合理範圍,他的實驗結果則對日後一整年照射服務的演算影響重大,因此要求他務必找出問題的根源,結果他重新檢討了三天,終於找出問題所在,我這樣管理員工確實是苛求他了,但不得不做。

最近的日常應用,則是我用它來分析每天固定時間測得的血壓讀數。年紀大了,每天使用血壓計量血壓並檢討結果是一件很有益的事情。如果數據不太離譜,我只記錄。如果我認為不太合理,我就先透過誤差函數進行統計分析,看看得到的結果是否合理? 不合理時,大膽略去,重新測量。出現問題,我就檢討問題。後來,經常發現,問題主要出現在一開機就測量的讀數上,解決辦法就是首次的讀數就棄用,每次測量三次,大部份就都沒有問題了。

自測血壓本不是個人該做的事情,讀數不好時絕對會影響心情,專業醫療人員測血壓時都用聽診器聽脈博脈動的聲音。家庭自測用的數位式簡易血壓計所測得的讀數只宜供作個人參考,不要過份解讀,畢竟我不是醫療從業人員,又沒用聽診器仔細聽脈音,套上抽真空束帶時,難免偶爾沒放準脈博偵測點,讀數就有偏差了。自測血壓跟做實驗一樣,需要注意測量時的正確性、穩定性、不受干擾性。分析讀數的技術是重溫本領,用自己設計的系統、自己設計的程式解讀問題,讓很多事情變得很有意義。

每天測量血壓後檢討生活,最近的體會是發現速食麵、高分子蛋白的花生、市面販售的濃湯粉包、含鹽份過度的馬鈴薯片等,都是上了年紀的人不宜接觸的食物。我每次在當天貪吃之後,洗澡前測一下血壓,同樣狀況的結果必定多出大約 10 個讀數,隔天不吃了,血壓就能降回大約是 120/80 的正常讀數,每天記錄,屢試不爽。我也發現多吃了一些南瓜湯、紅豆湯、羅宋湯都不影響血壓,也不會增加體重。這些事都是我最近的生活,有程式應用上的樂趣,也有滿意於健康的心情。

新改寫成的程式與執行結果列示如下:


 

\ 數據分析程式
\ 分光儀測量鋁錠中鐵含量(%)十次,分析這十個測試數據是否合理的程式。
\ 作者:曾慶潭 2011/4/21 保有一切版權,知會作者,附加本宣告則允許引用。
\ Copyright 2011/4/21 Ching-Tang Tseng
\ Permission is granted by the author to use this software
\ for any application provided this copyright notice is preserved.

20 VALUE SIZE

SIZE ARRAY DATA

REAL AVG
REAL SIGMA

INTEGER TOTAL
INTEGER I

: INPUT-DATA
{{ DATA ( 1 ) = 121.0 E 0 }}
{{ DATA ( 2 ) = 122.0 E 0 }}
{{ DATA ( 3 ) = 138.0 E 0 }}
[[ TOTAL = 3 ]]
\ {{ DATA ( 1 ) = 1.52 E 0 }}	\ (1)輸入待分析數據。
\ {{ DATA ( 2 ) = 1.46 E 0 }}
\ {{ DATA ( 3 ) = 1.61 E 0 }}
\ {{ DATA ( 4 ) = 1.54 E 0 }}
\ {{ DATA ( 5 ) = 1.55 E 0 }}
\ {{ DATA ( 6 ) = 1.49 E 0 }}
\ {{ DATA ( 7 ) = 1.68 E 0 }}
\ {{ DATA ( 8 ) = 1.46 E 0 }}
\ {{ DATA ( 9 ) = 1.83 E 0 }}
\ {{ DATA ( 10 ) = 1.50 E 0 }}
\ [[ TOTAL = 10 ]]		\ (2)數據的總量為10。
;

: RESET-DATA BASIC
10 FOR I = 1 TO SIZE
20 LET { DATA ( I ) = 0.0 E 0 }
30 NEXT I
40 LET TOTAL = 0
50 END
;

: AVERAGE BASIC
10 LET { AVG = 0.0 E 0 }
20 FOR I = 1 TO TOTAL
30 LET { AVG = AVG + DATA ( I ) }
40 NEXT I
50 LET { AVG = AVG / I>R ( TOTAL ) }
60 END
;

: DEVIATION BASIC
10 LET { SIGMA = 0.0 E 0 }
20 FOR I = 1 TO TOTAL
30 LET { SIGMA = SIGMA + ( DATA ( I ) - AVG ) * ( DATA ( I ) - AVG ) }
40 NEXT I
50 LET { SIGMA = SQRT ( SIGMA / ( I>R ( TOTAL ) - 1.0 E 0  ) ) }
60 END
;

: REPORT BASIC
10 RUN CR
20 RUN RESET-DATA
30 RUN INPUT-DATA
40 RUN AVERAGE
50 RUN DEVIATION
60 RUN CR
70 PRINT { " 平均值(Average value) = " ; AVG }
80 RUN CR
90 PRINT { " 標準誤差(Standard deviation) = " ; SIGMA }
100 RUN CR
110 END
;

REAL erfx   REAL erfy
REAL T   REAL D   REAL AUX

: ERROR-FUNCTION BASIC
10 LET { AUX = ABS ( erfx ) }
20 LET { T = 1.0 E 0 / ( 1.0 E 0  + 0.3275911 E 0  * AUX ) }
30 LET { D = EXP ( NEGATE ( erfx * erfx ) ) }
40 LET { erfy = 1.0 E 0  - D * T * ( 0.254829592 E 0 + T * ( -0.284496736 E 0       
         + T * ( 1.421413741 E 0  + T * ( -1.453152027 E 0  + 1.061405429 E 0  * T ) ) ) ) }  
50 IF  { erfx >= 0.0 E 0 } THEN 70
60 LET { erfy = NEGATE erfy }
70 END
;

REAL TIMES
INTEGER Discrimination
INTEGER FLAG
1.41421356237309 E 0 fconstant sqrt2

: MAIN BASIC
30 LET FLAG = 0
40 RUN REPORT
50 FOR I = 1 TO TOTAL
\ 60 LET { erfx = ( ABS ( DATA ( I ) - AVG ) ) / ( sqrt2 * sigma ) }  
60 LET { erfx = ( ABS ( DATA ( I ) - AVG ) ) / ( ( sqrt ( 2.0 E 0 ) ) * sigma ) }   
70 RUN ERROR-FUNCTION
80 LET { TIMES = 1.0 E 0 / ( 1.0 E 0 - erfy ) }
90 LET Discrimination = INT ( TIMES )
100 IF Discrimination > TOTAL THEN 120
110 GOTO 160
120 LET FLAG = FLAG + 1
130 PRINT " 統計分析顯示,數據 DATA( " ; I ; " ) = " ; { DATA ( I ) ; " 有問題。 " }
140 PRINT " 此值之誤差量,應該是測量 " ; Discrimination ; " 次以上才有可能出現一次。 "
150 PRINT " 但是這次測量, " ; TOTAL ; " 次就出現一次了,故有問題。"
160 NEXT I
170 IF FLAG <> 0 THEN 190
180 PRINT " 根據誤差函數核算結果顯示,所有數據的誤差量均在合理範圍之內。 "
190 END
;


\s

執行結果:

ching@ctt:~$ ./l

AMDX86 ciforth 5.3.0 
fload dataanaly.f
DATA : ISN'T UNIQUE                                                
I : ISN'T UNIQUE                                                
D : ISN'T UNIQUE                                                

Programs above \S have been loaded. 
 OK
main

平均值(Average value) = 1.27 E 2  

標準誤差(Standard deviation) = 9.5393920141 E 0  

統計分析顯示,數據 DATA( 3 ) = 1.38 E 2  有問題。 
此值之誤差量,應該是測量 4 次以上才有可能出現一次。 
但是這次測量, 3 次就出現一次了,故有問題。 OK