Rubyで浮動小数点の誤差対策!BigDecimalで安全な金額計算
先生と生徒の会話形式で理解しよう
生徒
「Rubyでお金の計算をすると、ちょっと変な数字になっちゃうことがあります。これってどうしてですか?」
先生
「それは浮動小数点(ふどうしょうすうてん)という仕組みの性質で起こります。コンピュータでは小数を完全に正確に表現できない場合があるんです。」
生徒
「じゃあ、お金の計算はどうすれば安全にできるんですか?」
先生
「RubyにはBigDecimalというクラスがあり、これを使うと小数点の誤差を避けて正確に計算できます。」
1. 浮動小数点の誤差とは?
浮動小数点(float)はコンピュータ内部で2進数として小数を扱うため、10進数で表したときに誤差が生じることがあります。たとえば、0.1 + 0.2を計算すると0.30000000000000004のように表示されることがあります。
puts 0.1 + 0.2
0.30000000000000004
このままだと金額計算には使えません。誤差があると、会計処理や請求計算で不具合が起きます。
2. BigDecimalで安全に計算
BigDecimalは文字列で小数を保持し、計算結果も正確に管理できるクラスです。Rubyで使うにはrequire 'bigdecimal'で読み込みます。
require 'bigdecimal'
require 'bigdecimal/util'
a = BigDecimal('0.1')
b = BigDecimal('0.2')
puts a + b
0.3
このように正確に計算できるため、金額や精密な数値処理で安心です。
3. BigDecimalの作り方と注意点
数値から直接BigDecimalを作ると、元の浮動小数点の誤差を引き継いでしまうことがあります。文字列で作るのが安全です。
# NG: floatから作ると誤差が残る
x = BigDecimal(0.1)
# OK: 文字列から作る
y = BigDecimal('0.1')
金額計算や統計処理では、必ず文字列で初期化することをおすすめします。
4. 実用例:お金の計算
BigDecimalを使うと、商品価格や合計金額の計算で誤差なく計算できます。
require 'bigdecimal'
require 'bigdecimal/util'
price1 = BigDecimal('120.50')
price2 = BigDecimal('99.99')
tax_rate = BigDecimal('0.10')
subtotal = price1 + price2
tax = subtotal * tax_rate
total = subtotal + tax
puts "小計: #{subtotal.to_s('F')}円"
puts "消費税: #{tax.to_s('F')}円"
puts "合計: #{total.to_s('F')}円"
小計: 220.49円
消費税: 22.049円
合計: 242.539円
浮動小数点では0.01円単位の誤差が出ることがありますが、BigDecimalを使えば正確に計算できます。
5. BigDecimalの活用ポイント
- 金額計算や会計処理で誤差を防ぐ
- 統計計算や科学技術計算で精密な小数を扱う
- 浮動小数点と組み合わせる場合は注意する
このようにRubyのBigDecimalを使えば、浮動小数点の誤差に悩まされず、安全な数値計算が可能です。