C 的捨進位函式：rint() 與 round()
Tags: c
Published: 2022-07-09 05:20:51

###### tags: `C`

# C 的捨進位函式：rint() 與 round()

在 math.h 中有 [`round()`](https://en.cppreference.com/w/c/numeric/math/round) 和 [`rint()`](https://en.cppreference.com/w/c/numeric/math/rint) 兩個看起來很像的函式, 但是卻有細微差異, 簡單來說, `round()` 就是我們熟悉的四捨五入, 而 `rint()` 則是可變更捨進位方式的版本。

## round() 函式

`round()` 採用四捨五入, 例如：

```c
#include<stdio.h>
#include<math.h>

int main() {
  printf("%.20f\n", round(4.3));
  printf("%.20f\n", round(-4.3));
  printf("%.20f\n", round(3.5));
  printf("%.20f\n", round(-3.5));
}
```

執行結果如下：

```
4.00000000000000000000
-4.00000000000000000000
4.00000000000000000000
-4.00000000000000000000
```

## rint() 函式

`rint()` 函式猛看和 `round()` 好像一樣, 例如把剛剛的範例從 `round()` 改成使用 `rint()`：

```c
#include<stdio.h>
#include<math.h>

int main() {
  printf("%.20f\n", rint(4.3));
  printf("%.20f\n", rint(-4.3));
  printf("%.20f\n", rint(3.5));
  printf("%.20f\n", rint(-3.5));
}
```

執行結果如下：

```
4.00000000000000000000
-4.00000000000000000000
4.00000000000000000000
-4.00000000000000000000
```

沒有什麼特別的, 但是如果執行以下的程式：

```c
#include<stdio.h>
#include<math.h>

int main() {
  printf("%.20f\n", rint(4.5));
  printf("%.20f\n", rint(-4.5));
}
```

執行結果如下：

```
4.00000000000000000000
-4.00000000000000000000
```

咦？為什麼是捨去小數而不是進位呢？這是因為在預設的捨進位模式下, `rint()` 是捨進位到較近的整數, 但如果小數部分剛好就是 0.5, 則是會捨進位成偶數。以剛剛的例子來說, 如果是 3.5, 因為 3 是奇數, 所以會進位變成 4；但若是 4.5, 因為 4 本身就是偶數, 所以就變成捨去小數不進位了。

要注意的是, 因為是捨進位到比較近的整數, 所以並不是只看小數第一位, 例如：

```c
#include<stdio.h>
#include<math.h>

int main() {
  printf("%.20f\n", rint(4.5001));
  printf("%.20f\n", rint(-4.5001));
}
```

執行結果就跟前一個例子不一樣了：

```
5.00000000000000000000
-5.00000000000000000000
```

這是因為 4.5001 比較靠近 5, 所以會進位成整數 5。只有在小數**剛好就是介於兩個整數之間的 0.5** 時, 才會依照捨進位成偶數的法則。

## 變更捨進位模式

`rint()` 對於 0.5 要捨位還是進位是可以變更的, 你可以透過定義在 fenv.h 內的 [`fesetround()`](https://en.cppreference.com/w/c/numeric/fenv/feround) 設定, 共有以下幾種模式：

|模式常數|說明|
|-----|----|
|[FE_DOWNWARD](https://en.cppreference.com/w/c/numeric/fenv/FE_round)|捨進位到比較小的整數, 結果就會跟 [`floor()`](https://en.cppreference.com/w/c/numeric/math/floor) 一樣|
|[FE_TONEAREST](https://en.cppreference.com/w/c/numeric/fenv/FE_round)|捨進位到較近的整數, 遇到 0.5 時捨進位成偶數|
|[FE_TOWARDZERO](https://en.cppreference.com/w/c/numeric/fenv/FE_round)|捨進位到靠近 0 的整數, 也就是無條件捨去小數, 就跟 [`trunc()`](https://en.cppreference.com/w/c/numeric/math/trunc) 一樣|
|[FE_UPWARD](https://en.cppreference.com/w/c/numeric/fenv/FE_round)|捨進位成比較大的整數, 也就是 [`ceil()`](https://en.cppreference.com/w/c/numeric/math/ceil)|

預設的模式就是 `FE_TONEAREST`, 這個模式主要是想要解決統計數值時四捨五入會讓原本分布平均的資料在捨進位後分布不平均, 因為 0.5 一律進位, 可能會讓數值偏高, 因此改成捨進位成偶數, 就相當於讓 0.5 有一半的機會進位、一半的機會捨去。

如果更改捨進位模式, 例如：

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

int main() {
  fesetround(FE_DOWNWARD);
  printf("%.20f\n", rint(4.3));
  printf("%.20f\n", rint(-4.3));
  printf("%.20f\n", rint(3.5));
  printf("%.20f\n", rint(-3.5));
  printf("%.20f\n", rint(4.5));
  printf("%.20f\n", rint(-4.5));
}
```

執行結果就會變成：

```
4.00000000000000000000
-5.00000000000000000000
3.00000000000000000000
-4.00000000000000000000
4.00000000000000000000
-5.00000000000000000000
```

就會捨進位成較小的整數, 這個結果就跟你用 `floor()` 一樣：

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

int main() {
  printf("%.20f\n", floor(4.3));
  printf("%.20f\n", floor(-4.3));
  printf("%.20f\n", floor(3.5));
  printf("%.20f\n", floor(-3.5));
  printf("%.20f\n", floor(4.5));
  printf("%.20f\n", floor(-4.5));
}
```

執行結果如下：

```
4.00000000000000000000
-5.00000000000000000000
3.00000000000000000000
-4.00000000000000000000
4.00000000000000000000
-5.00000000000000000000
```

## 小結

使用 `rint()` 因為會受到捨進位模式設定的影響, 我個人建議就直接使用函式名稱直白明確的 `round()`、`ceil()`、`floor()`、`trunc()` 比較好。