施藝
(四川省成都水文水資源勘測局,成都,611130)
水文數據是國家重要的基礎信息資源,而水文工作則是行業基礎數據的主要來源。為保證水文資料成果質量,《水文資料整編規范》(SL/T 247-2012)對水文數據中各項要素計算、取用精度做出了要求:各項要素數值應按規范要求的取用精度取位,取用精度位數后一位數字,采用“四舍六入”方法取舍。為更好地適應水文信息化和現代化發展,本文對水文數據的收舍以及在編程中的實現進行了分析探討。
有效數字(Significant Figure),是在整個計算過程中大致維持重要性的近似規則。對于一個近似數,從左邊第一個非零數字起,到精確到的位數止,所有的數字都叫作這個數的有效數字。簡單地說,把一個數字前面的0都去掉,從第一個正整數到精確的數為止所有的數字都是有效數字。
在水文數據中,各項要素數值的取用精度多采用“有效數字+小數不過多少位”的方式進行取位,如流量、比降、輸沙率采用“3位有效數字,小數不過3位”,洪水量、徑流量采用“4位有效數字,小數不過4位”。取用精度位數后一位數字,采用“四舍六入”方法取舍。
四舍六入,全稱四舍六入五成雙,是一種比較精確、比較科學的計數保留法。具體規則為取用精度位數后1位小數小于5則舍,大于5則入,等于5時若其后有非零位數仍入,無非零尾數則視取用的末位數字的奇偶取舍,為奇則入,為偶則舍。
在國外,四舍六入又被稱為Banker's round(即銀行家舍入),大部分的編程軟件都使用的是這種方法,但具體到大家日常使用的辦公軟件卻很少使用四舍六入,基本都是四舍五入。比如EXCEL里的Round函數采用的是四舍五入,而VBA中的Round函數則采用的是四舍六入。
前文提到,日常使用的辦公軟件如OFFICE、WPS等基本都是采用四舍五入,無法滿足水文數據的收舍要求。如在EXCEL中,Round函數采用的是四舍五入,如果想要四舍六入,需要通過設置復雜的公式,即便通過公式能達到目的,也需要針對性地設置數值相應的收舍位數,對每個值進行單獨設置,工作量很大,也很繁瑣。在編程中實現水文數據的收舍,既能為進一步開發水文相關的軟件提供支撐,同樣也能通過與現有辦公軟件相結合,擴展現有辦公軟件的使用局限,方便普通職工的使用。
限于會占用大量篇幅,文章只采用了一到兩種語言來進行介紹,雖然不同語言的代碼格式不同,但思路是通用的。
3.2.1 有效數字的實現
在常用的編程語言中,除C++不支持Banker's round(即四舍六入)外,其余常用的編程語言都使用四舍六入。按有效數字進行收舍則只有C++可以通過setprecision函數以及R語言可以通過signif函數可以做到。要按有效數字進行四舍六入,可以通過封裝自定義函數來實現。
實現思路為:設數值為i,有效數字為d,數值的位數為k。先判斷數值i的位數k,大于1時k為正,取i整數部分位數;小于1時k為負,取i小數點后出現多少個連續的零。d減去k,得到需要收舍的位數n。按round(i,n)對數值i進行四舍六入。
遇到的問題1:n為負時round函數無法工作,即收舍只能對數值的小數部分進行收舍,無法根據有效數字對整數部分進行收舍,如12850的3位有效數字應收舍為12800。解決方案:對數值乘以10的n次方,然后按取整進行四舍六入,再除以10的n次方,這樣就能對整數部分也進行有效數字的收舍了。
遇到的問題2:判斷數值i的位數k,開始是用字符串進行處理,方法為如果i大于1,取i小數點左邊的字符串的長度;如果i小于1,取i小數點右邊數值的長度減去i小數點右邊字符串的長度。如0.0012小數點右邊字符串為0012、字符串長度為4,而0012的值為12、字符串長度為2,2減去4就得出i的位數k為-2。但這樣處理代碼效率不高,影響大量數據處理的效率。解決方案:經過翻閱資料,發現一個小技巧,通過對數值(絕對值)求其以10為底的對數,可以很方便地知道它的位數。如log10(0.0012)等于-2.9,向上取整后即為數值i的位數-2,log10(6554)等于3.8,向上取整后即為數值i的位數4。
以Java語言為例,實現代碼如下。

為方便普通職工在EXCEL中直接使用該函數,接下來采用VBA語言進行編譯,將函數封裝為自定義公式,實現代碼如下。

采用VBA語言進行編譯需要注意的是:①EXCEL中默認采用的是浮點數運算,由于計算機在處理數據時存在二進制與十進制的轉換,直接對數值進行處理往往會出錯,需要通過CDec函數將數值轉換為Decimal類型數據再進行處理;②VBA中沒有log10函數,也沒有向上取整,我們通過Log(i)/Log(10)來代替log10函數,再通過Int(i)+1計算得到位數。
3.2.2 水文數據收舍的實現
水文數據的收舍,不僅需要滿足按有效數字進行四舍六入,還要按不超過多少位小數進行收舍。因此,還需要對自定義函數進行加工。
實現思路為:設置可選項最大小數位數d1,默認為0(不設置最大小數位數),可選項有效數字d2,默認為0(不設置有效數字)。當有效數字大于0,如果最大小數位數d1不小于有效數字d2減去數值i的位數k,需要收舍的位數n則為d2減去k,否則n等于最大小數位數d1。其余步驟參考之前代碼。為方便在其他代碼中調用該函數(下文會有介紹),可以設置全局變量Ⅳalue、iDigits,將收舍后的值賦予Ⅳalue、收舍的位數賦予iDigits。
以VBA語言為例,實現代碼如下。

3.3.1 復合條件的應用
在水文數據的計算中,除了按有效數字以及最大小數位數進行四舍六入,某些要素數值是通過復合條件確定取用精度。比如流速為數值不小于1取3位有效數字,小于1取2位有效數字,小數不過3位;水深為數值不小于100記至1,小于100不小于5記至0.1,小于5記至0.01。針對這樣的情況,可以對自定義函數進行再開發,通過設定具體的要素類型,對數值進行處理。接下來的代碼演示以下5種類型的取用精度及運算。
類型1:取3位有效數字,小數不過3位。如流量、徑流模數、輸沙率等。
類型2:取3位有效數字,小數不過2位。如斷面面積、流量系數等。
類型3:不小于1,取3位有效數字;小于1,取2位有效數字,小數不過3位。如流速。
類型4:不小于100,記至1;小于100,不小于5,記至0.1;小于5,記至0.01。如水深。
類型5:取3位有效數字;不小于5,小數不過1位;小于5,小數不過2位。如水面寬、閘門開啟總寬、平均堰寬等。

3.3.2 按刊印要求進行設置
在水文資料整編中,除了對數值的精度有要求,對數據的刊印格式也有同樣的要求,通過調用自定義函數可以方便我們對數據進行處理。以下代碼用于在表格保存時對表格中的數值按刊印要求進行位數的設置。

代碼解析:首先,將有數字的單元格設置為一個Range對象,遍歷這些單元格,根據單元格所在的位置將其設置為對應的類型,比如表格中第三列是水深數據、第四列是面積、第五列是流速、第六列是流量,將第三列設為類型4、第四列設為類型2、第五列設為類型3、第六列設為類型1;其次,通過調用Sw_Round函數,判斷數值是否滿足相應類型的取用精度,不滿足則更改為收舍后滿足取用精度的值,比如流量10.25應改為10.2;最后,根據Sw_Round函數得到的數值應該保留的位數,將其設置為對應格式比如流速0.5應寫為0.50。
本文簡要介紹了通過程序語言封裝自定義函數實現水文數據的收舍,以及對相關函數的擴展應用,為進一步開發水文相關軟件提供一定的支撐,同樣也能通過與現有辦公軟件相結合方便普通職工使用。以期解決以往遇到收舍與刊印要求時單個要求單個處理,缺乏可移植性,重復工作量大以及數據處理缺乏可靠性的問題。當然,這只是筆者的一點經驗之談,旨在拋磚引玉,希望能讓更多的專業技術人員受到啟發,將編程更多地應用到水文信息化建設中。