← 所有文章

PLC 整數運算的順序 精度與中間溢位

· 站長

用同一比例比較三種計算順序,再用最終結果小但中間乘積大的案例檢查位寬。

最後結果放得下 不代表中間乘積放得下

同樣算 3×100÷8,先乘再除得到整數37,先算3÷8卻可能先變0,最後仍為0。數學上等價的式子,到了有限位寬與整數除法就不一定等價。這篇教你在每一步標上型別、範圍與捨棄資訊,讓比例計算不再只靠幾筆正常值判斷。

案例採有號整數,除法規則明訂為向零截斷;16位範圍為−32768~32767,32位範圍為−2147483648~2147483647。這是推算契約,不宣稱所有PLC運算指令都採相同暫存位寬。實作要先查目標CPU、指令與語言的中間結果規則。

寫法 中間結果 最後整數 差異原因
(3×100)÷8 300 37 37.5在最後截斷
(3÷8)×100 0 0 比例先被截掉
3×(100÷8) 12 36 100÷8先失去0.5

這不是鼓勵所有式子都先乘。先乘通常減少過早截斷,卻增加中間溢位風險;你要同時控制精度與範圍。若比例必須保留37.5,可以改成放大10倍存375,但那也需要重新計算中間乘積,而不是只把畫面小數位加一。

把正負算例逐步攤開

先以寬型別的理想整數運算推導,再決定實作型別。負數不能直接套只針對正數的補0.5技巧,本篇一律向零截斷,讓−37.5得到−37。若需求是最近整數或向負無限大,應另定規格,不要混在比例公式裡。

a b c (a×b)÷c (a÷c)×b
3 100 8 37 0
−3 100 8 −37 0
7 10 3 23 20
−7 10 3 −23 −20
8 100 8 100 100

最後一列兩種順序剛好相同,因此它不適合單獨拿來證明程式正確。測試資料要含有餘數的比例、負值以及上下界,不要只選可整除的漂亮數字。記錄的對象也要包含中間乘積,否則錯誤可能早已發生,最後一個暫存器只剩難以解釋的結果。

整數常數同樣影響型別。若編譯器把常數與輸入當作窄整數,中間乘法可能先在窄範圍計算,再指派到寬結果。把結果變數改成DINT不必然等於整個運算已使用DINT;應在乘法前明確擴寬運算元,並依平台文件確認。

一個結果很小但中間很大的案例

令 a=30000、b=100、c=1000。正確寬整數乘積是3000000,最後結果3000,完全落在16位範圍;但中間乘積超過32767。如果你的實作把乘積存在16位,資訊會先失真。用更大的目的變數接收最後3000,無法補救已經算壞的乘積。

檢查位置 預期值 需要的判斷
a 30000 可放16位
b 100 可放16位
a×b 3000000 不可放16位;可放32位
除以1000 3000 結果可放16位,但須先確認中間值
窄化回16位 3000 轉換前再次檢查範圍
  1. 取得輸入最大最小值,而不是只看目前監看值。

  2. 在乘法前把運算元轉到足夠寬的型別;中間暫存也用同樣位寬。

  3. 先檢查除數不為0,並處理最小負整數除以−1的溢位邊界。

  4. 做除法後依需求取整,最後才窄化輸出。

  5. 保存原值、乘積、商、餘數或錯誤旗標,讓診斷能指出失敗在哪一步。

32位也有上限。例如50000×50000=2500000000,超過32位有號最大值,即使之後再除50000仍只得到50000。選擇更寬型別前要確認平台支援,不能把「改成32位」當所有比例公式的終點。

何時選固定小數 何時選浮點

若資料解析度固定為0.1,可用整數單位表示十分之一,並把倍率放進欄位名稱或資料字典;輸入3、倍率1000、除數8,得到375,解讀成37.5。這種方式容易定義小數精度,但倍率會放大中間乘積。數值跨度很大或演算法有多段比例時,可以評估浮點,仍須接受二進位表示與取整邊界。

需求 可評估方法 不能忘記
固定一位小數 以0.1為整數單位 倍率、位寬和寫回格式
跨很大數值範圍 REAL/LREAL 有效位數與目標支援
比例常數可約分 先合法約分再乘 不能用近似除法任意重排
需完全可重現尾數 明訂固定小數與取整 每一步的溢位與負數行為

CODESYS相關官方說明提醒,中間結果可能按目標原生位寬計算,指派或顯式轉換時才截取到目標型別。因此「兩個16位數相乘一定以16位溢位」也不能泛稱所有平台;本篇16位失真案例是明確限制乘積暫存為16位的反例。你的驗收要以選定CPU與編譯器輸出為準。

參考:ABB CODESYS 中間結果的原生位寬

參考:CODESYS Numeric constants 整數與浮點常數

把一個比例式拆成可驗收的運算鏈

拿到比例需求後,先寫原始單位與目標單位。例如原始計數要換成千分比,倍率就屬於公式的一部分,不是顯示設定。接著列出輸入最大值、最小值、乘數與除數範圍,逐段計算可能出現的最大絕對值。只有這些資料齊全,才能決定中間型別。

用本篇三萬乘一百再除一千的例子,在監看區分別放原值、擴寬後值、乘積與商。紙上預期乘積為三百萬,商為三千。如果原值正常而乘積已經異常,就先查乘法的運算型別與暫存位置;不要往後調整除數,因為那只會把錯誤重新包裝成另一個數字。

接著測不能整除的輸入。例如三乘一百再除八,最後有半個單位不能放在整數結果中。把這半個單位的去向寫進規格:直接捨去、依明訂規則取整,或以更小的整數單位保存。三種方式都能設計,但不能在不同分支使用不同方式卻沿用同一個欄位名稱。

負數測試要和正數成對。若需求允許反向流量或負溫度,就不能只測正值。本文明訂除法向零截斷,負的非整除結果會往零靠近。若實際平台使用其他規則,應先調整實作或規格,不能把符號差異當成感測器零點誤差而任意補償。

除數也可能由參數提供,因此每次採用新參數前都要檢查。除數為零時停止正常運算,保留原始資料並回報原因;不要先做除法才看結果是否合理。如果分母原本是很小的浮點數,雖然不等於零,也可能把結果放大到不可接受範圍,需另訂有效範圍。

完成紀錄時要同時保存公式版本與參數版本。只保存最後的商,之後很難分辨是公式順序改變、倍率修改,還是型別窄化造成差異。先把運算鏈在離線資料上逐段復算,再到目標工程軟體核對相同輸入;沒有執行的測試欄位應保持未測,不要填入推算值冒充實測。

完成結果與故障排查

完成後應能復算37、0、36三個順序結果,負數案例方向一致,30000×100÷1000得到3000,除數0則回報錯誤而不執行。將50000×50000列為寬度檢查案例,不要真的交給未保護的窄運算測試。

症狀 先查哪裡
比例突然全為0 是否在乘法前做整數除法
小值正常 大值變負數 中間乘積位寬與窄化位置
負數與正數不對稱 截斷/取整規則是否一致
只有某CPU結果不同 原生暫存寬度、常數型別與編譯版本

問:先乘一定較準嗎?它通常延後截斷,但可能先溢位,必須一併檢查。問:結果用DINT就夠嗎?要在運算前確保中間型別也足夠,不能只改接收欄位。問:除數0時回傳0好嗎?只有規格明訂且另帶無效旗標才可用,否則0會被當成正常結果。

適用型號與限制:本篇為一般整數比例設計,具體平台行為參考CODESYS/ABB官方文件。Q06UDVCPU的16位與32位乘除指令、目的裝置佔用字數及溢位狀態另依三菱手冊核對;本文沒有提供可直接下載的Q程式。

延伸閱讀


使用 PLC 工具箱 →