Pythonで競プロするときの知識メモ3（整数・小数）

## 1. はじめに
Python[^1]でAtCoderを始めたので、覚えておきたいと思ったことをメモしていきます。
基本的には自分用ですが、Pythonで競プロを始める方の参考になればという気持ちも少しだけあります。

今回は整数・小数についてです。

## 2. 整数
Pythonで整数を扱うときは[数値型のint](https://docs.python.org/ja/3.11/library/stdtypes.html#numeric-types-int-float-complex)を使います。
整数の精度は無制限[^2]のため、32bit整数や64bit整数ということを意識する必要はありません。

以降、基本的には正の整数同士の演算を想定しています。（一部負の数まで含めて検証したものもあります）

### 四則演算
[公式ドキュメント](https://docs.python.org/ja/3.11/library/stdtypes.html#numeric-types-int-float-complex)で組み込み数値型が対応している演算について表にまとめられています。
計算の順序も一般的な数式と同じです。

```
a = 6 + 2   # a: 8
b = 6 - 2   # b: 4
c = 6 * 2   # c: 12
d = 6 / 2   # d: 3.0 -> 「/」での割り算の結果は小数（float型）になる
e = 6 // 2  # e: 3

f = 6 * 2 + 10 // (4 - 2)  # f: 17
```

### 割り算の切り下げ、切り上げ
#### 切り下げ（切り捨て）
演算子`//`を使います。
結果は常に負の無限大の方向に丸められます。

mathモジュールの[floor関数](https://docs.python.org/ja/3.11/library/math.html#math.floor)でも良さそうです。（※）

```
a = 1 // 2        # a: 0
b = -1 // 2       # b: -1  ※ 0にはならない
c = 1 // (-2)     # c: -1  ※ 0にはならない
d = (-1) // (-2)  # d: 0
```

※ 自分で試した訳ではありませんが、一部誤差が出るケースもあるようです。（[参考](https://qiita.com/iwbchi/items/a0296e15076482e074f6)）

#### 切り上げ
「整数aを整数bで割って切り上げた結果」は`-(-a//b)`で求めることができます。[^3]
mathモジュールの[ceil関数](https://docs.python.org/ja/3.11/library/math.html#math.ceil)でも良さそうです。（※）


| a | b | a / b | math.ceil(a / b) | -(-a//b) |
| - | - | - | - | - |
| 5 | 3 | 1.6666666666666667 | 2 | 2 |
| -5 | 3 | -1.6666666666666667 | -1 | -1 |
| 5 | -3 | -1.6666666666666667 | -1 | -1 |
| -5 | -3 | 1.6666666666666667 | 2 | 2 |
| 6 | 3 | 2.0 | 2 | 2 |
| -6 | 3 | -2.0 | -2 | -2 |
| 6 | -3 | -2.0 | -2 | -2 |
| -6 | -3 | 2.0 | 2 | 2 |

※ 誤差が出る場合があるかは未検証

### 割り算の余りを求める
演算子`%`を使います。

```
a = 5 % 3  # a: 2
b = 6 % 3  # b: 0
```

### 累乗を求める
演算子`**`を使います。
[pow関数]()でも良さそうです。

```
a = 5**2       # a: 25
b = pow(5, 2)  # b: 25
```

### 2, 8, 16進数 ⇔ 10進数の変換
#### 2, 8, 16進数 -> 10進数
方法はいろいろありそうですが、[intのコンストラクタ](https://docs.python.org/ja/3.11/library/functions.html#int)をよく使っています。

```
# "11"を第2引数で指定した数の進数として、10進数に変換する。
# 第1引数は文字列で指定する。

a = int("11", 2)    # a: 3
b = int("11", 8)    # b: 9
c = int("11", 10)   # c: 11
d = int("11", 16)   # d: 17
e = int("11", 37)   # 例外が発生 -> ValueError: int() base must be >= 2 and <= 36, or 0
```

※ intを使うと、2~36進数までの数を10進数に変換できます。

#### 10進数 -> 2, 8, 16進数
組み込み関数の[bin関数](https://docs.python.org/ja/3.11/library/functions.html#bin)、[oct関数](https://docs.python.org/ja/3.11/library/functions.html#oct)、[hex関数](https://docs.python.org/ja/3.11/library/functions.html#hex)を使います。
[format関数](https://docs.python.org/ja/3.11/library/functions.html#format)や[f-string](https://docs.python.org/ja/3.11/tutorial/inputoutput.html#tut-f-strings)を使うとプレフィックスをなくすこともできます。

変換後の値は文字列になります。

```
a = bin(30)            # a: 0b11110
b = format(30, "#b")   # b: 0b11110
c = oct(30)            # c: 0o36
d = format(30, "o")    # d: 36 -> プレフィックス"0o"がつかない
e = hex(30)            # e: 0x1e
f = format(30, "#X")   # f: 0X1E -> 大文字に変換
```

## 3. 小数
Pythonで浮動小数点数を扱うときは[数値型のfloat](https://docs.python.org/ja/3.11/library/stdtypes.html#numeric-types-int-float-complex)や[decimalモジュール](https://docs.python.org/ja/3.11/library/decimal.html)を使います。

### 四捨五入
[round関数](https://docs.python.org/ja/3.11/library/functions.html#round)や[decimalモジュールのquantize関数](https://docs.python.org/ja/3.11/library/decimal.html#decimal.Decimal.quantize)を使います。

round関数では境界（= `5`を四捨五入する）の場合に一般的な四捨五入と異なる挙動になります。
ただ、round関数の方が手軽に使えるため、十分小さな値を加えることで上記の問題を回避しつつ、roundで四捨五入を行うこともあります。（[参考](https://atcoder.jp/contests/abc226/editorial/2888)）

例）正の小数の小数第3位を四捨五入する場合
```
from decimal import Decimal, ROUND_HALF_UP

x = 2.675
print(round(x, 2))
print(Decimal(str(x)).quantize(Decimal("0.01"), ROUND_HALF_UP))
```
| x | round | dicimal |
| - | - | - |
| 2.670 | 2.67 | 2.67 |
| 2.671 | 2.67 | 2.67 |
| 2.672 | 2.67 | 2.67 |
| 2.673 | 2.67 | 2.67 |
| 2.674 | 2.67 | 2.67 |
| 2.675 | **2.67** | 2.68 |
| **2.675005** | 2.68 | 2.68 |
| 2.676 | 2.68 | 2.68 |
| 2.677 | 2.68 | 2.68 |
| 2.678 | 2.68 | 2.68 |
| 2.679 | 2.68 | 2.68 |


以上です。
最後まで読んでいただきありがとうございました。


## 参考資料
* [Pythonドキュメント](https://docs.python.org/ja/3.11/library/intro.html)
* [アルゴリズム実技検定 公式テキスト エントリー ~ 中級編](https://www.amazon.co.jp/%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E5%AE%9F%E6%8A%80%E6%A4%9C%E5%AE%9A-%E5%85%AC%E5%BC%8F%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88-%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA%E3%83%BC-%E4%B8%AD%E7%B4%9A%E7%B7%A8-Compass-Books%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA/dp/483997277X)
* [pythonで正の整数の除算における切り捨て、切り上げを正しく行う方法](https://qiita.com/iwbchi/items/a0296e15076482e074f6)
* [Pythonで小数・整数を四捨五入するroundとDecimal.quantize](https://note.nkmk.me/python-round-decimal-quantize/)

## その他の知識メモ

* [Pythonで競プロするときの知識メモ1（標準入出力）](https://qiita.com/tanakasoboro/items/dcb51c1db5eb53defa4d)
* [Pythonで競プロするときの知識メモ2（文字列）](https://qiita.com/tanakasoboro/items/f455a30519fa64af0b89)

[^1]: 投稿日時点で使用しているPythonのバージョンは`3.11.9`です。
[^2]: ドキュメントにも`Integers have unlimited precision`とあり、マシンのメモリが許す限り大きな値を扱えるということですかね。
[^3]: 正の整数同士の割り算の切り上げだともう少しいろいろな計算方法がありそうですが、負の整数まで含めるとこの方法が良さそうです。