「9 / 5と9 // 5はなぜ結果が違うの?」「0.1 + 0.2がぴったり0.3にならないのはなぜ?」――Pythonを書き始めると、数値まわりで一度は引っかかるポイントがいくつかある。
この記事では、Pythonの数値型の基本である 真偽値(bool)・整数(int)・浮動小数点数(float) の3つを中心に、リテラルの書き方から演算子・優先順位、型変換、桁数の上限、浮動小数点数の誤差まで、基礎からまとめて解説する。
この記事でわかること
- Pythonの数値型はbool・int・floatの3つが基本(複素数complexもあるが詳しくは扱わない)
- bool型が実は整数の仲間であること
- 整数(int)のリテラルの書き方と、2進数・8進数・16進数への相互変換
- 整数・浮動小数点数の演算子、優先順位、複合代入演算子
int()・float()による型変換の挙動- Pythonのintに桁数の上限がない理由
- 浮動小数点数(float)が誤差を持つ理由(IEEE 754と最短表現の仕組み)と、誤差への対処法
- よくあるミスと、数値型の使い分け
1. Pythonの数値型の全体像
Pythonの組み込み数値型は、次の4つの型がある。
| 型 | 役割 | リテラルの例 |
|---|---|---|
| bool | 真偽値(True/False) | True, False |
| int | 整数(桁数に上限なし) | 42, -7, 1_000_000 |
| float | 浮動小数点数(小数) | 3.14, 2.7e5 |
| complex | 複素数 | 3+4j, 5j |
複素数を表す complex 型は、特別な分野以外ではあまり使われない。ここでは、日常的によく使うbool・int・floatの3つを詳しく説明する。
2. ブール値(bool型)
bool 型は「真(True)」または「偽(False)」という2つの真偽値を表すデータ型だ。bool型の値はTrueとFalseのいずれかしかない。これらの値はそのままでも使用されるが、bool型はif文やwhile文などの制御構造にともに使われる。
2.1 ブール値リテラル
ブール値リテラルもTrueまたはFalseだけだ。
>>> True + 2
3
>>> False + 5.0
5.0
>>> bool(0)
False
>>> bool(-45)
True
0以外の数値はすべて「真」とみなされ、0(や0.0)だけが「偽」になる。この性質は if 文などの条件判定でよく使うことになるので、頭の片隅に入れておくと後で理解がスムーズだ。
3. 整数(int型)
Pythonで整数のデータ型はint型の一つしかない。正確にはPythonのbool型はint型のサブクラスなので、整数型と言えなくもないが、普通は整数を扱う用途では使われない。
3.1 リテラルの書き方
整数リテラルは、数字を並べて書く。先頭に + や - をつけることもできる。
>>> 123
123
>>> 0
0
>>> +123
123
>>> -123
-123
桁の区切りにアンダースコア _ を使うこともできる。Pythonはアンダースコアを単に無視する。
>>> 1_000_000
1000000
Pythonではあらゆるデータがオブジェクトである。整数は int 型のオブジェクトだ。
>>> type(3)
<class 'int'>
type() は、渡されたオブジェクトの型(クラス)を返す組み込み関数だ。<class 'int'> という表示は、int クラスのインスタンスであることを意味する。Pythonでは「型」と「クラス」は同じ意味なので、これは「int型のオブジェクトである」ということだ。
3.2 intの桁数に上限はない
他のプログラミング言語には、複数の整数型を持つものがある。たとえばC言語には short・int・long などの型があり、それぞれが表現できる整数の範囲が異なる。一方Pythonの int は、メモリが許す限りどんなに大きな整数でも扱えるため、整数を表す型は int ひとつだけだ。有名な例だが、googol(1のあとに0が100個続く数)もそのまま計算できる。
>>> googol = 10 ** 100
>>> googol * googol
100000000000000000000...(中略)...0000000
他の多くの言語で起きる「整数オーバーフロー」を、Pythonでは基本的に気にしなくてよいというのは大きな特徴だ。
3.3 2進数・8進数・16進数
ここまでのリテラルはすべて10進数の整数だったが、Pythonでは2進数・8進数・16進数の整数リテラルも、次のプレフィックスを付けて書ける。
| プレフィックス | 基数 | 例 |
|---|---|---|
0b / 0B | 2進数 | 0b10 → 2 |
0o / 0O | 8進数 | 0o10 → 8 |
0x / 0X | 16進数 | 0x10 → 16 |
>>> 0b10 # 2進数: 1×2¹ + 0×2⁰
2
>>> 0o10 # 8進数: 1×8¹ + 0×8⁰
8
>>> 0x10 # 16進数: 1×16¹ + 0×16⁰
16
16進数では10〜15を表すのに a〜f(大文字・小文字どちらでも可)を使う。桁の区切りにアンダースコアを入れることもできる。
>>> 0b1111_1111 # アンダースコアで桁をグループ化できる
255
>>> 0o377
255
>>> 0xFF
255
>>> 0xff # 小文字でも同じ
255
逆に、10進数の整数を各基数の文字列に変換する bin()・oct()・hex() という関数も用意されている。
>>> value = 65
>>> bin(value)
'0b1000001'
>>> oct(value)
'0o101'
>>> hex(value)
'0x41'
負の数を渡すと、マイナス符号が先頭に付いた文字列が返る。
>>> bin(-10)
'-0b1010'
>>> oct(-56)
'-0o70'
>>> hex(-42)
'-0x2a'
逆に、各基数の文字列を10進数の整数に変換したい場合は、int() の第2引数に基数を指定する。
>>> int('1010', 2)
10
>>> int('17', 8)
15
>>> int('ff', 16)
255
補足として、bin()・oct()・hex() は結果に必ず 0b・0o・0x のプレフィックスを付けるが、プレフィックスなしの文字列がほしい場合は format() やf文字列でも同じ結果が得られる。
>>> format(255, 'x') # プレフィックスなし
'ff'
>>> f'{255:#x}' # プレフィックスあり
'0xff'
3.4 整数の演算
Pythonでは、整数同士の演算に次の演算子が使える。
| 演算子 | 意味 | 例 | 結果 |
|---|---|---|---|
+ | 加算 | 5 + 8 | 13 |
- | 減算 | 90 - 10 | 80 |
* | 乗算 | 4 * 7 | 28 |
/ | 浮動小数点数除算 | 7 / 2 | 3.5 |
// | 整数除算(切り捨て) | 7 // 2 | 3 |
% | 剰余 | 7 % 3 | 1 |
** | べき乗 | 3 ** 4 | 81 |
整数リテラルの先頭に付ける + や - も、厳密には演算子だ。被演算子を1つしか取らないため単項演算子と呼ばれる。
>>> x = 34
>>> y = +x
>>> y
34
>>> z = -y
>>> z
-34
+ を付けても値は変わらない。- を付けると符号が反転する。
+・- は2つの被演算子をとる二項演算子としても使える。
>>> 1 + 2
3
>>> 5 - 3
2
>>> 7 - 10
-3
複数の演算子を組み合わせて使うこともできる。同じ優先順位の演算子は左から右へ順に計算される。
>>> 1 + 2 + 3
6
>>> 10 - 3 - 2
5
>>> 1 - 2 + 3 - 4 + 5
3
除算は少し変わっていて、/ と // の2つの演算子がある。/ は結果を浮動小数点数(float型)で返す。
>>> 7 / 2
3.5
>>> 6 / 2
3.0
// は端数を切り捨て、結果を整数(int型)で返す。
>>> 7 // 2
3
>>> -7 // -2
3
>>> -7 // 2
-4
演算結果が負の値になる場合、Pythonでは負の無限大方向に切り捨てる。そのため3番目の例では、-3.5を負の無限大方向で最も近い整数に切り捨てた -4 になる。他の言語では0方向に切り捨てるものもあるので注意が必要だ。
% は除算の余りを返す。
>>> 7 % 2
1
>>> -7 % -2
-1
>>> -7 % 2
1
Pythonには「a % b の符号は常に b(除数)の符号と同じになる」という規則がある。また、a // b と a % b の間には常に a == (a // b) * b + a % b という関係が成り立つ。
商と剰余を同時に求めたいときは、組み込み関数の divmod() が使える。divmod() は結果をタプルで返す。
>>> 7 // 2
3
>>> 7 % 2
1
>>> divmod(7, 2)
(3, 1)
べき乗演算子 ** もある。
>>> 3 ** 2
9
>>> 2 ** 8
256
3.5 演算子の優先順位
演算子には優先順位がある。たとえば次の式の結果はどうなるだろうか。
2 + 3 * 4
答えは 14 だ。乗算は加算より優先されるという、算数で習った規則と同じだ。加算を先に計算したい場合は、() で括る。
>>> 2 + 3 * 4
14
>>> (2 + 3) * 4
20
Pythonの算術演算子の優先順位は、高いものから並べると次のようになる。
| 優先順位 | 演算子 | 説明 |
|---|---|---|
| 高 | ** | べき乗 |
| ↑ | +x, -x | 単項プラス・単項マイナス |
*, /, //, % | 乗算・除算・切り捨て除算・剰余 | |
| ↓ | +, - | 加算・減算 |
| 低 |
優先順位が同じ演算子は左から順に計算される。
>>> 7 // 2 * 3
9
もう1つ例を見てみよう。
>>> -3 ** 2
-9
これは、おそらく意図した結果ではないだろう。単項マイナス演算子より ** 演算子のほうが優先順位が高いため、先に 3 ** 2 が計算され、その結果の符号が反転する。意図した結果にするには、次のように括弧を使う。
>>> (-3) ** 2
9
3.6 変数を含む演算
これまでの演算では、結果の値はすぐに捨てられていた。結果の値をあとで使いたい場合は変数に保存しておく。= の右辺の演算が実行され、結果の値が左辺の変数に代入される。もちろん右辺の式に変数を使うことができる。
>>> x = 2 + 3
>>> x
5
>>> y = x * 2
>>> y
10
= は数学の「等しい」とは意味が異なる。= の右辺の値を左辺の変数に代入するという意味で、代入に先立ち右辺の式が計算される。変数 x は 5 という整数の値を持つので、整数リテラルと同じように演算に使える。
次のようにすると、変数の値を上書きできる。
>>> count = 0
>>> count
0
>>> count = count + 1
>>> count
1
>>> count = count + 1
>>> count
2
最初に count に 0 が代入され、そのあとは count の値に 1 を加えて、その結果を再び count へ代入している。このような操作はよく行われるので、便利な演算子が用意されている。
>>> count = 0
>>> count += 1
>>> count
1
>>> count += 1
>>> count
2
これは先ほどの操作とほぼ同じ結果になる。「ほぼ」というのは、count = count + 1 では count が2回評価されるのに対して、count += 1 では count が1回しか評価されないためだ。通常この違いは無視できる。
+= のような演算子は複合代入演算子と呼ばれ、前述の算術演算子にはそれぞれ対応する複合代入演算子がある。
| 演算子 | 意味 | x += 1 は概ね次と等価 |
|---|---|---|
+= | 加算代入 | x = x + 1 |
-= | 減算代入 | x = x - 1 |
*= | 乗算代入 | x = x * 1 |
/= | 除算代入 | x = x / 1 |
//= | 切り捨て除算代入 | x = x // 1 |
%= | 剰余代入 | x = x % 1 |
**= | べき乗代入 | x = x ** 1 |
3.7 intへの型変換
int() 関数を使うと、他の型の値を整数に変換できる。浮動小数点数を渡すと小数部分は切り捨てられ、数字だけの文字列も整数に変換できる。
>>> int(12.3)
12
>>> int(-45.6)
-45
>>> int('64')
64
>>> int('1_000_000')
1000000
数値ではない文字列を渡すと ValueError になる点には注意が必要だ。
>>> int('64Byte')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
int('64Byte')
~~~^^^^^^^^^^
ValueError: invalid literal for int() with base 10: '64Byte'
4. 浮動小数点数(float)
浮動小数点数とは、3.14 や 1.5 のように小数点を含む数値のことだ。Pythonではこのような数値を float 型で表し、実数の近似値を表現する。
4.1 リテラルの書き方
小数点を持つ数値はfloat型として扱われる。先頭に + や - をつけることもでき、桁区切りにアンダースコア _ も使える。整数部あるいは小数部のどちらか一方を省略することもできる。
>>> 3.14
3.14
>>> +3.14
3.14
>>> -1.23
-1.23
>>> 10.
10.0
>>> .03
0.03
>>> 1_000_000.0
1000000.0
指数表記(e または E を使う書き方)も使える。
>>> 1e3
1000.0
>>> 1.166e-05
1.166e-05
>>> -0.31E12
-310000000000.0
浮動小数点数の型は float だ。
>>> type(3.14)
<class 'float'>
4.2 浮動小数点数は近似値である
ほとんどの場合、float型の実際の値は近似値になる。これは、無限に存在する実数を有限のビット数で表現しなければならないためで、Pythonに限らず多くのプログラミング言語に共通する問題だ。浮動小数点数は近似値であるため、計算結果が直感に反することがある。
>>> 0.1 + 0.2
0.30000000000000004
Pythonのfloat型は、IEEE 754の倍精度浮動小数点数として実装されている。これは有限のビット数でデータを保持するため、数直線上のすべての実数を表現できるわけではなく、飛び飛びの値しか表現できない。
0.1 とコードに書いても、実際には0.1に最も近い、表現可能な浮動小数点数が代わりに保存される。この値を10進数の小数に直すと、次のようになる。
>>> from decimal import Decimal
>>> Decimal(0.1) # 0.1が内部的に持っている"本当の"値
Decimal('0.1000000000000000055511151231257827021181583404541015625')
つまり、Pythonで 0.1 と書いても、実際にはそれとほんのわずかに違う値が保存されているのだ。
round() 関数の結果が直感に反することもある。
>>> round(2.675, 2)
2.67 # 2.68を期待するかもしれないが…
これは、2.675 という値自体が実際には2.675よりわずかに小さい値として保存されているためだ。
科学技術計算などでは、これらのわずかな誤差はたいてい許容される。金額の計算など誤差が許されない場合には、decimal.Decimal を利用できる。浮動小数点数の値が近似値であることから、次のような点に注意が必要だ。
1. 誤差を許容した比較をする
== で直接比較せず、差が十分小さいかどうかで判定する。
>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
2. 表示桁数を丸めて確認する
>>> round(0.1 + 0.2, 10)
0.3
3. 正確な10進数計算が必要ならdecimalモジュールを使う
金額計算など、誤差が許されない場面ではこちらが有効だ。
>>> from decimal import Decimal
>>> Decimal('0.1') + Decimal('0.2')
Decimal('0.3')
4. 分数として正確に扱いたいならfractionsモジュールを使う
>>> from fractions import Fraction
>>> Fraction(1, 10) + Fraction(2, 10)
Fraction(3, 10)
参考:
decimal・fractionsモジュールは、書籍22章「科学におけるPython」でも詳しく紹介されている。より突っ込んだ使い方を知りたい場合はそちらも参考にしてみてほしい。
補足:なぜ0.1は「0.1」と表示されるのか
0.1 という10進数リテラルは、内部的には 0.1000000000000000055511151231257827021181583404541015625 という2進浮動小数点数として保存される。しかし、この64ビットの2進数値と同じビットパターンに戻る10進数の文字列は、実は無数に存在する。次のような文字列は、どれも同じfloat値にパースされる。
>>> 0.1 == 0.10000000000000001
True
>>> float('0.1') == float('0.1000000000000000055511151231257827021181583404541015625')
True
Python(バージョン3.1以降)は、こうした候補の中から最も短く、float() に戻したときに元と完全に同じ値になる文字列を選んで表示するアルゴリズムを採用している。0.1 という3文字は、その条件を満たす最短の表現なので、これが表示されるというわけだ。
>>> x = 0.1
>>> repr(x)
'0.1'
>>> float(repr(x)) == x # 表示された文字列を戻すと、元の値と完全一致する
True
もし repr() が内部の全桁(0.1000000000000000055511151231257827021181583404541015625)をそのまま表示していたら、人間にとって非常に読みにくくなる。かといって単純に「小数点以下◯桁で丸める」方式だと、情報が失われて元の値に戻せなくなる可能性がある。「最短かつ完全に元に戻せる」という基準を採用することで、次の2つの利点を両立させている。
- 人間が読みやすい(
0.1) - 情報の損失がない(
float('0.1')で完全に元通りになる)
普段は 0.1 のように最短表現がそのまま整った数字になるため誤差に気づかないが、計算を行うと、計算結果を表す最短の文字列がたまたま長くなることがあり、そこで誤差が表面化する。
>>> 0.1 # 単体では最短表現がきれいな "0.1"
0.1
>>> 0.1 + 0.2 # 計算結果の"真の"2進数値に対応する最短表現が長くなる
0.30000000000000004
つまり「0.1 という値自体に誤差がない」わけではなく、「0.1 を表示するときの最短表現がたまたま短くきれいになる」だけで、内部的な誤差は常に存在している。それが 0.1 + 0.2 のような計算で顕在化する、というのが正確な理解だ。
4.3 浮動小数点数の演算
浮動小数点数でも、整数で紹介した算術演算子がそのまま使える。
| 演算子 | 例 | 結果 |
|---|---|---|
+ | 3.5 + 2.1 | 5.6 |
- | 5.7 - 1.2 | 4.5 |
* | 2.5 * 4.0 | 10.0 |
/ | 7.5 / 2.5 | 3.0 |
// | 9.5 // 2.0 | 4.0 |
% | 9.5 % 2.0 | 1.5 |
** | 2.0 ** 3.0 | 8.0 |
>>> -3.5
-3.5
>>> +3.5
3.5
切り捨て除算(//)は、intのときと同様、結果をより小さい方向(負の無限大方向)に切り捨てる。結果はfloat型だ。
>>> 9.5 // 2.0
4.0
>>> -9.5 // 2.0
-5.0
剰余も、「a % b の符号は常に b(除数)の符号と同じになる」という規則は整数のときと同じだ。
>>> 9.5 % 2.0
1.5
>>> -9.5 % 2.0
0.5
べき乗は平方根の計算にも使える。
>>> 2.0 ** 3.0
8.0
>>> 2.0 ** 0.5 # 平方根の計算にも使える
1.4142135623730951
平方根専用の関数として math.sqrt() も用意されており、こちらを使うのが一般的だ。
>>> import math
>>> math.sqrt(2)
1.4142135623730951
** 0.5 と math.sqrt() はほぼ同じ結果になるが、math.sqrt() のほうが意図が明確で、わずかに高速・高精度な場合もあるため、平方根を求めたいだけならこちらを使うのが一般的な書き方だ。
4.4 floatへの型変換
float() 関数で他の型を浮動小数点数に変換できる。
>>> float(True)
1.0
>>> float('98.6')
98.6
>>> float('1.0e4')
10000.0
整数と浮動小数点数を混ぜて計算すると、Pythonが自動的に整数側を浮動小数点数へキャストする。
>>> 4 + 7.0
11.0
>>> 10 + 3.5
13.5
5. よくあるミスと注意点
① 整数の桁区切りにカンマを使ってしまう
1,000,000 はカンマ区切りの整数ではなく、3要素のタプルになる。桁を区切りたいときはアンダースコア _ を使おう。
② / と // を混同する
/ は常にfloatを返し、// は端数を切り捨ててintを返す。整数の結果が欲しいのか、正確な商が欲しいのかで使い分けが必要だ。
③ -5 ** 2 の結果を勘違いする
指数演算子は単項マイナスより優先順位が高いため、-5 ** 2 は -25 になる。(-5) ** 2 (25)と結果が異なるので、意図がある場合は括弧で明示しよう。
④ 浮動小数点数どうしを == で比較する
0.1 + 0.2 == 0.3 は誤差の影響で False になる。厳密な一致判定が必要な場面では、math.isclose() を使うか、decimal モジュールの利用を検討しよう。
>>> 0.1 + 0.2 == 0.3
False
6. 数値型の使い分け
| 場面 | おすすめの型 |
|---|---|
| 個数・回数・インデックスなど、割り切れる量を扱う | int |
| 長さ・重さ・金額の概算など、小数を扱う | float |
| 条件式の結果や、真偽のフラグを持たせたい | bool |
| 端数を一切許さない金額計算をしたい | decimal.Decimal (22章) |
| 分数のまま正確に計算したい | fractions.Fraction (22章) |
7. 練習問題
書籍3章の復習課題を参考にした、対話モードで試せる練習問題だ。
問題1:1分は60秒、1時間は60分だ。対話型インタプリタを電卓として使い、1時間が何秒かを計算してみよう。
問題2:問題1の結果を変数 seconds_per_hour に代入し、それを使って1日(24時間)が何秒かを計算してみよう。
問題3:問題2の結果を変数 seconds_per_day に代入しよう。そのうえで、seconds_per_day を seconds_per_hour で割った結果を、/ と // の両方で計算し、結果がどう違うか確かめてみよう。
解答例を見る
# 問題1
seconds_per_hour = 60 * 60
print(seconds_per_hour) # 3600
# 問題2
seconds_per_day = seconds_per_hour * 24
print(seconds_per_day) # 86400
# 問題3
print(seconds_per_day / seconds_per_hour) # 24.0 (float)
print(seconds_per_day // seconds_per_hour) # 24 (int)
/ はfloatを返すので 24.0、// はintを返すので 24 になる。今回はちょうど割り切れる値なので、小数点以下を除けば同じ結果になっていることが確認できる。
まとめ
- Pythonの数値型は bool・int・float の3つが基本。boolは実は整数の一種
- intは桁数無制限で、
_で桁区切り、0b/0o/0xで2進・8進・16進表記ができる /はfloatを返し、//はintを返す。この違いはよく混同されるので要注意- 切り捨て除算
//と剰余%は、負の数では負の無限大方向に丸められる - 浮動小数点数の誤差はIEEE 754による2進数表現が原因。
math.isclose()やdecimal・fractionsモジュールで対処できる - 演算子の優先順位に迷ったら、遠慮なく括弧を使うのが実践的なコツ
次回は、この章の続きである条件分岐(if)を扱う予定だ。
参考資料
- 『入門Python3 第2版』Bill Lubanovic 著、鈴木駿 監訳、長尾高弘 訳(オライリー・ジャパン) 3章「数値」
- Python 公式ドキュメント:組み込み型 --- 数値型 int, float, complex
- Python 公式ドキュメント:字句解析 --- 数値リテラル
- Python 公式ドキュメント:組み込み関数 --- bin(), oct(), hex(), divmod(), round(), format()
- Python 公式ドキュメント:math --- 数学関数
- Python 公式ドキュメント:decimal --- 10進固定小数点数及び浮動小数点数の算術
- Python 公式ドキュメント:fractions --- 有理数