はじめに
地図を使ってアプリを作成すると緯度と経度を扱うことが多いかと思います。
緯度と経度のような小数を扱う場合、どのデータ型を使えば良いのかを調べたところ、
float型とdecimal型という二つが出てきたのでそれぞれについてまとめてみました。
※ 注意:本記事におけるfloat型について
プログラミング言語やデータベースの種類によって「float」の言葉の範囲が異なります。
本記事では「RailsにおけるDBのfloat型」を前提として解説しています
float型とdecimal型とは
まずfloat型とdecimal型について調べてみるとそれぞれ以下のような定義がされています。
-
float型
数値(浮動小数点数) -
decimal型
精度の高い小数
「浮動小数点数」や「精度の高い少数」というのはどういうことでしょうか?
ということでもう少し詳しく調べてみると…
- float型は浮動小数点を2進数で扱うため、正確に数字が表現できない場合がある
- decimal型は浮動小数点を10進数で扱うため、精度を高く表現できるようです。
なんとなくイメージができるような気がしますが、
しっくりこないので、具体的にどう違うのか順を追って考えてみましょう。
小数の表現について
コンピュータ内部で小数を表す場合、下記の構成で表現することができます。
仮数部×基数^{指数部}
例えば10進数で0.75を表す場合は以下のようになります。
0.75 = 75×10^{-2}
- 仮数部:75
- 基数:10
- 指数部:-2
このように小数を表現する方法を「浮動小数点数」と呼びます。
2進数と10進数の違い
では、2進数はなぜ正確に小数を表現できない場合があるのでしょうか?
2進数で小数を表す場合も、10進数と同様に($2^{-n}$)の組み合わせで表現します。
0.5 = 2^{-1}
0.25 = 2^{-2}
ここで0.3を2進数と10進数でそれぞれ表現してみましょう
-
10進数の場合:$3×10^{-1}$ (
0.3でぴったり表現できる) -
2進数の場合:
0.0100110011001…(無限に続いてしまう)
10進数の0.3は、2進数に変換すると循環小数になってしまいます。
コンピュータのメモリには限りがあるため、どこかで数値を打ち捨てざるを得ません。
2進数で表現できない小数は近似値で扱われることになり、
これが丸め誤差(計算誤差)が発生する原因です。
なぜdecimal型だと精度が高いのか
0.3 という値は、10進数であれば $3 × 10^{-1}$ という有限の式でぴったり表現できます。
2進数では有限の桁数でピッタリ表現できない数字であっても、10進数構造で扱えば有限の桁数で正確に保持できます。
つまり、「10進数そのもので扱うdecimal型」であれば、人間が扱う一般的な小数を誤差なく表現できるのです。
※ なお1/3のようにそもそも10進数でも循環小数になる場合は、ある程度のところで切り捨てる必要があります。
まとめ
| 項目 | float型(Ruby: Float) |
decimal型(Ruby: BigDecimal) |
|---|---|---|
| 内部の処理方法 | 2進数で扱う | 10進数で扱う |
| 精度 | 誤差(丸め誤差)が生じる可能性がある | 正確に小数を保持・計算できる |
| 処理速度 | 高速(CPUが直接演算) | 低速(ソフトウェアで制御) |
| 主な用途 | アニメーション演算、位置情報(誤差が許容できる場合) | 金融・決済処理、税計算など(誤差が許されない場合) |
参考資料
まとめるにあたって下記の技術記事を参考にさせていただきました。
なぜfloatで丸め誤差が生じるのか
(Rails)データベースのデータ型種類について勉強してみた!
MySQLに緯度経度を保存する際の、カラム型の選び方
(Rails)データ型「float型とdouble型」
integer/float/rational/bigdecimalの何が違う?
Rubyによる小数とFloatとBigDecimalについて…(初心者向け)