今度は、Python の「数値型」について、色々と考えてみることにする。
「文字列型」の話(2)でも書いたようにPython のデータ型は、PostgreSQLよりも大雑把。
今回の話、少々、話が複雑で、しかも、結果が微妙に異なるかもしれない。
エラーが出なければ、おそらく、問題は無い。以下の話は、基礎知識を得るための参考。
本日のプログラムは、パソコンの機種やOSにも依存するので、一致するとは限らない。
実は、Python の「数値型」は4つしか無い。これらは「組み込み型」と呼ばれる型の一種。
PostgreSQL と対比させてみると、相互に変換できるものは3つしかない。
また、Python には、「複素数型(Complex)」があるのも興味深い。
PostgreSQL9.1
|
Python2.7
|
狭範囲(smallint)
{範囲:-32768〜+32767}
|
該当なし
|
並範囲(integer)
{範囲:-32768〜+32767}
|
整数型(int)
{範囲:-2147483648〜+2147483647}
|
広範囲(bigint)
{範囲:-2147483648〜+2147483647}
|
長整数型(long)
{無制限}
|
固定小数点(decimal/numeric)
{小数点までの範囲:131072桁} {小数点以降の範囲:16383桁} |
該当なし
|
単精度(real)
{精度:4バイト(6桁精度)}
|
該当なし
|
倍精度(double)
{精度:8バイト(15桁精度)}
|
浮動小数点型(long)
{精度:8バイト(15桁精度)}
|
該当なし
|
複素数型(complex)
|
「複素数型」は、少し複雑な数値計算を必要とする場合に必要となる。
Python は、科学計算などにも用いられることがある。特徴がよく現れている。
この型については、今回は説明しない。数値計算が必要な時にでもまとめよう。
さて、実を言うと、Pythonの「数値型」というのは、
C言語と呼ばれる別のプログラミング言語の「数値型」を用いている。
それを、Pythonが借用しているのである。つまり、上記4つの数値型はC言語のもの。
呼び方も全く違う。統一してくれれば良いのだが、何かの事情があるのだろう。
残念ながら、私は、なぜ呼び方が異なっているかの理由は知らないが、
どちらも、何となくは、言いたいことが解るので、まぁ、良いことにする。
いつも通りに話が逸れた。
順番に見てみると、PostgreSQL で「integer」だったものが「int」になっている。
「integer」をフルネームで呼ぶか、略称で呼ぶかという違い。これは納得。
「bigint」が「long」になっている。「大きい」と考えるか、「長い」と考えるか。
名前の違いで見ると、「浮動小数点型」の部分。PostgreSQLの方では、二つあった。
Python の方は一つだけ。実は、「Float」というのは、浮動小数点の総称。
PostgreSQL では、単精度と倍精度の二種類があるので分けている。
さて、次に実際の「有効桁数」と「精度」について見てみる。
Python の数値型は、C言語の数値型を借用していることはすでに述べた。
では、C言語のどの型を借用しているのか?
- int ← C言語のlong型
- long ← C言語には無い?
- float ← C言語におけるdouble型
Pythonという言語は、比較的最近に開発された言語であるので、
近年のコンピュータの能力に合せているのかもしれない。
なるほど、ここまで、整理できたところで、
実際にPython を立ち上げて、数値型について理解を深めてみる。
とりあえず、「文字列型」の話の手順でPythonのコンソールを立ち上げる。
最初にするべきことは、「>>>」の後ろに、以下をコピペ。
import sys
これは、「sys」という名前の「部品群」の読み込み作業。
Python では、様々な外部の部品を読み込んで使うことができる。
一般的には、「ライブラリ」あるいは「パッケージ」と呼ぶ。
さて、この「sys」という名前の「部品群」を読み込むと、
OSが提供する、つまり、システムが提供する様々な機能が使えるようになる。
だから「System」の略で「sys」。コンピュータの世界では略称をよく使う。
さて、この「sys」の部品群が提供してくれている「機能」を使うためには、
「sys.機能名」というように、読み込んだ部品群の名前の後ろに「.」をつけ、
さらに、その後ろに使いたい機能の名前を書く。
使いたい機能はどうやって知ることができるか?
実を言うと、APIリファレンスと呼ばれるマニュアルを読む必要がある。
これには、ちょっとしたコツがあって、読み方が解ればプログラミングは簡単。
しかし、今回の話は、プログラミングの入門講座ではない。
基本的な説明は終わったので、先に進むとしよう。
とにかく、「sys」を読み込むことができれば、次の一行をコピペ。
sys.maxint
この実行結果は以下の通り。
>>> import sys
>>> sys.maxint
2147483647
この結果で出てきたのが、Pythonにおける「int型」の最大値。
この値は、C言語のlong型であり、PostgreSQLの「bigint型」に等しい。
実を言うと、最小値を求める関数は無い。これに「-1」を掛けた値が最小値となる。
では、この最大値を超えるとどうなるかを実験してみる。
次の行をコピーして実行してみる。
現在表示されているのが最大値なので、これに「1」を足してみる。
sys.maxint+1
この実行結果は以下の通り。
>>> sys.maxint+1
2147483648L
いや、よく見ると数値の最後に「L」が付いている。
実は、Python の場合、限界を超えると自動的にlong型に変換される。
こういった機能を持つプログラミング言語は珍しく、
もちろん、PostgreSQL においてもエラーが発生する。
したがって、Pythonのint型の限界を超えた値をPostgreSQL には入れれない。
では、いよいよ、厄介な「浮動小数点」について見てみる。
「浮動小数点」において注意すべきことは何か?答えは「有効精度」の問題。
小数点以下で保証される桁数がいくつか?ということ。
これを間違えると、計算するときに誤った結果を得ることになる。
とりあえず、誤差というのが何か?という問題から考えてみる。
何も考えずに、以下の二行のコマンドをコピペ。
float("10.000000000000001")
少し強引な例ではあるが...。
このコマンドの実行結果は以下の通り。
>>> float("10.000000000000001")
10.000000000000002
文字列型の話(2)では、「"」で囲むと文字列になると解説した。
つまり、一度、文字列として「数字」を作り、それをfloat型に再変換している。
そうそう。「キャスト」と呼ばれる処理のことであった。結果は?
浮動小数点は、あくまで、近似的に値を表しているため、
このように元の数値が元の通りに再生できるとは限らない。
場合によっては、有効精度以内であっても生じることがある。
次は、「浮動小数点型」の最大と最小の値を確認してみる。
float型の情報を得るためには、もう少し難しいなコマンドが必要になる。
整数型に比べて、見ないと行けない値が多い。
sys.float_info
同様に、この実行結果は以下の通り。
見易いように以下の出力は少し整形してある。
通常は、ひと続きで出力される。
>>> sys.float_info
sys.float_info(max=1.7976931348623157e+308,
max_exp=1024,
max_10_exp=308,
min=2.2250738585072014e-308,
min_exp=-1021,
min_10_exp=-307,
dig=15,
mant_dig=53,
epsilon=2.220446049250313e-16,
radix=2,
rounds=1)
何やら、一杯出てきた。このうち、「max」と「min」は10進数での「指数」の限界値。
「1.7976931348623157」の「10」の「308乗」が最大値であり、
「2.2250738585072014」の「10」の「308乗」が最小値である。
ところで、「max」と「min」の部分の表示方法、覚えておいた方が良い。
Excel などでも、非常に大きいあるいは小さい数値を扱うとこの表示になる。
これは、「0」の数が多いと数えるのが面倒だから。つまり、「エラー」では無い。
さてさて、またまた、話が逸れた。
つまり、上記のことが解れば、「max_10_exp」と「min_10_exp」は、大体、予想がつく。
要するに、10進数における「指数部」の限界値であり、その上限と下限である。
この値よりも「-1乗」だけ小さい値の範囲までが扱うことができる。
ということは、なるほど、「max_exp」と「min_exp」は、2進数での「指数」の限界値。
2進数における「指数」の取り得る範囲は、最大で「1024」であり、
負の方向には、最小で「-1021」までの値を取ることができる。
いわゆる、「精度」というのは、「dig」の部分であり「15桁精度」であることを示している。
「radix」というのは、指数における「低」のことで、したがって、「2」。これも良い。
「mant_dig」は、仮数部に保持できる2進数(ビット)での桁数。
あと二つ残っているか。「epsilon」は、float型で表現できる「1」の次の値。
なぜ必要か理解できない人もいるかもしれないが、厳密な科学計算では精度確認に必要。
そして最後の「round」は、何やら、「丸め込み」の方法らしい。良く解らない。
さて、「浮動小数点」の精度の問題は、よく理解しておいた方良い。
ここで簡単な実験をしてみる。どのような実験か?
「0.1」を「10」回足すという作業をPython で書いてみる。
ついでなので、計算式の立て方についても触れておくことにする。
まずは、以下のように考えることができる。小学生でも作れる計算式。
0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 = 1.0
このまま計算しても良いのだけれど、もう少し、この式を一般化してみる。
「0.1」の部分を「x」という記号に置き換えると、以下のように表せる。
次は、中学生レベルでの式の立て方。
ここで右下に付いている数字や記号のことを「添字」と呼ぶ。
一番前から数えて、不特定番目を「i」とし「n」番目までの数を計算するという意味。
また、「sum」や「x」というのは、状況によって値が変化するので「変数」と呼ぶ。
さて、最後に高校生レベルの計算式の作り方。
多くの自称「文系」の人達が吐き気を催すかもしれない「Σ」を使う。
「Σ」は足し算の記号。実は、エクセルの足し算の記号も「Σ」になっているはず。
「Σ」の記号の下には「i=1」と書いてある。つまり、不特定番目は「1」から開始。
そして、「Σ」の上の部分で、全部で「n」個の数が存在していることが示されている。
「Σ」の横には、「」と書かれているので、「i」を一つづつ繰り上げながら「n」回」足す」。
さて、さらに、このような一般式が立てれたところで、
今度は、当初の計算の目的である「0.1」と「10」を入れてみる。
以下のように書くことができる。少々、特殊な書き方であるが。
ここまでの話はこれから書くプログラムを理解する上で重要。
この数式を実際に動かすようにプログラムすることを「実装」と呼ぶ。
では、まずは「変数」の初期値を与えてやる。初期値を必要とするのは、「sum」。
「sum」には最終的に結果が格納されるのだが、計算途中の中間結果もここに入るので、
最初に何も入っていない状態にしないといけない。これを「初期化」と呼ぶ。
sum = 0
n = 10
まずは、上記をコピーしてコンソールに貼り付ける。
そして、次に「Σ」の部分の計算。「iを1〜10の範囲で計算する」ということは、
英語にすると「for i in range of 10」なので、これを「n」を用いてPython 風に書きなおす。
for i in range(n):
最後の「:」を忘れないように。このような「for」で始まる部分を「ループ文」と呼ぶ。
「ループ文」では、処理を「入れ子」に書くことができる。プログラミングの基本機能。
Pythonにおける「入れ子」の状態は、「インデント」と呼ばれる「空白」で表す。
さて、この部分までコピーして実行すると、次の行には、「...」という行が出てくる。
今度は、「Σ」の後ろの部分を書く。ここで重要なことが一つ。
Python では、コマンドの前の「空白の数」が重要。今回は、空白を「4」つ入れる。
x = 0.1
sum = sum + x
スペース「空白」の部分も含めて「厳密」に一行ずつコピーする。
上手く行かなった人もいるかもしれない。そのような人は、
以下に、まとめたコマンドを準備したので、以下をコピペして実行。
sum = 0
n = 10
for i in range(n):
x = 0.1
sum = sum + x
ここまでを貼りつけて、コンソールの先頭が「...」の状態であれば、
もう一度、エンターキーを押して「>>>」となるようにする。
この状態では、まだ、結果は表示されないので、最後に、次のように入力して実行する。
sum
そして、この結果は以下の通りになる。
>>> sum
0.9999999999999999
あれ〜!何か、計算間違いでもしたのだろうか「1」にならない...。
実は、この結果、Python のエラーやバグではない!
これは正しい計算結果。浮動小数点の性質が原因。
「0.1」は、厳密には「0.1」では無いので、微細な誤差が累積するのである。
なお、上記の結果を「文字列型」に直には...
str(sum)
であり、その実行結果は以下の通り。
>>> str(sum)
'1.0'
となる。ちゃんと、「1.0」に戻っている。
少々、不思議な話かもしれないが、このような特徴がある。
0 件のコメント:
コメントを投稿