C 語言的「四捨六入五成雙」

最近在編寫單元測試的時候,我需要一個將浮點數「四捨六入五成雙」的函式。一開始我想:這很容易:只要把整數部分和小數部分用 modf 分開,再寫一些判斷式就可以了。modf 的函式原型如下:

double modf(double input, double* integral_part);

不過寫到一半覺得要判斷的條件很多(必須考慮正負數、整數部分的奇偶判斷),我就在想:libm 應該要有「四捨六入五成雙」的函式才對呀。

四捨五入:round

接著我就看到 round 函式:

double round(double input);

然而我仍然無法通過單元測試。仔細檢查後,我發現 libm 的 round 函式是「四捨五入(遠離零)」。換句話說:round(2.5) 的回傳值是 3.0不是我想要的 2.0

四捨六入五成雙:rint

最後我才看到 rint 函式:

double rint(double input);

這個函式會根據 Rounding Mode 回傳與 input 對應的整數。如果 Rounding Mode 是 FE_TONEARESTrint 函式就會是「四捨六入五成雙」。

另外,Rounding Mode 是全域變數,需要透過 fegetround 函式與 fesetround 函式存取。

範例程式碼如下:

#include <fenv.h>
#include <math.h>
#include <stdio.h>

int main() {
  fesetround(FE_TONEAREST);
  for (double val = -2.0; val <= 2.0; val += 0.25) {
    printf("input=%.2f  round=%.2f  rint=%.2f\n",
           val, round(val), rint(val));
  }
}

輸出結果:

input=-2.00  round=-2.00  rint=-2.00
input=-1.75  round=-2.00  rint=-2.00
input=-1.50  round=-2.00  rint=-2.00
input=-1.25  round=-1.00  rint=-1.00
input=-1.00  round=-1.00  rint=-1.00
input=-0.75  round=-1.00  rint=-1.00
input=-0.50  round=-1.00  rint=-0.00
input=-0.25  round=-0.00  rint=-0.00
input=0.00  round=0.00  rint=0.00
input=0.25  round=0.00  rint=0.00
input=0.50  round=1.00  rint=0.00
input=0.75  round=1.00  rint=1.00
input=1.00  round=1.00  rint=1.00
input=1.25  round=1.00  rint=1.00
input=1.50  round=2.00  rint=2.00
input=1.75  round=2.00  rint=2.00
input=2.00  round=2.00  rint=2.00

附錄:modf/round/rint 的變型

libm 的 modfroundrint 依照參數型別,各自有給 float 型別的 f 變型與給 long double 型別的 l 變型(後綴):

參數型別 modf round rint
double modf round rint
float modff roundf rintf
long double modfl roundl rintl

roundrint 依照回傳型別,各自有給 long 型別的 l 變型與給 long long 型別的 ll 變型(前綴):

參數型別 回傳型別 round rint
double double round rint
long lround lrint
long long llround llrint
float float roundf rintf
long lroundf lrintf
long long llroundf llrintf
long double long double roundl rintl
long lroundl lrintl
long long llroundl llrintl

參考資料