9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Ruby】1 - 0.8 = 0.2 にしたい (浮動小数点誤差)

Last updated at Posted at 2019-06-30

###小数点の演算で誤差がでてしまった

> 1 - 0.8
=> 0.19999999999999996

0.2 を期待しているのに、誤差が生じてしまってます。

1に小数点つけてもおなじ

> 1.0 - 0.8
=> 0.19999999999999996

###BigDecimalを使う方法

bigdecimalをrequireすると良い、とあったのでやってみました。

> require  'bigdecimal'
> BigDecimal("1") - BigDecimal("0.8")
=> 0.2e0

BigDecimalの引数はString型にして、誤差のない数を演算機に伝えてる(と理解した)。
理由はわからないけど答えに指数部分がついてきました。String#to_fでいい感じの小数点表示にする。

> require  'bigdecimal'
> (BigDecimal("1") - BigDecimal("0.8")).to_f
=> 0.2

####答えに整数が欲しいとき

例えば消費税計算とか。1800 * 1.08 の答えは1944が返ってきて欲しいのに、、、

> 1800 * 1.08
=> 1944.0000000000002

こういう時にBigDecimal使う。

> require 'bigdecimal'
=> true
> (BigDecimal("1800") * BigDecimal("1.08")).ceil
=> 1944

※上記に使われているFloat#ceilは「自身と等しいかより大きな整数のうち最小のものを返す」。
BigDecimal使わないとこうなる。


> (1800 * 1.08).ceil
=> 1945

###Rational(有理数)クラスを使う方法
コメント@obelisk68 さんに教えていただきました。ありがとうございます!
そのままコピペします。

"
BigDecimal でよいのですが、Rational(有理数)クラスを使ってもよいです。ここでは0.8の浮動小数点演算が問題なので、これを有理数(分数と考えてかまいません)に直して計算します。このとき、Float#to_rではなく、Float#rationalizeを使うとよいです。


[1] pry(main)> i = 1 - 0.8.rationalize
=> (1/5)
[2] pry(main)> i.to_f
=> 0.2

あるいは0.8が決め打ちならば、有理数リテラルを使うと簡単に書けます。

[3] pry(main)> j = 1 - 8/10r
=> (1/5)
[4] pry(main)> j.to_f
=> 0.2

消費税計算もこんな感じ。

[5] pry(main)> (1800 * 108/100r).ceil
=> 1944

"

数値リテラル初めて意識しました。有理数も、、、

有理数(分数と考えてかまいません)

こういう分かりやすい説明ができる人になりたい。
浮動小数点誤差の解消法方法はいろいろあるようなので、慣れてきたら他にも調べてみたいと思います。

###参考
[Ruby]消費税計算にはBigDecimalを使いましょう
instance method String#to_f
instance method Float#ceil
class Rational

9
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?