Rubyにて数値計算をするうえで、複雑な計算をすることがあります。乱数の発生、平方根、sinやcosなどの三角関数・・・などなど。
Rubyではそれらを実装するためのクラスが提供されており、私たちはそれらのおかげで簡単に複雑な計算ができます。
数値計算といえば科学技術っぽく聞こえますが、もっと身近な例でいうと、金額計算も数値計算です。金額計算はお金の計算である以上、誤差や計算誤りは絶対に許されません。
では、Rubyではそのような厳密な計算はどうするのでしょうか?
本記事では、Randomクラスをはじめとした様々なクラス、端数処理、Ruby独特の分数計算を解説します。
Rubyの各種クラス、モジュール
様々なクラスが用意されていますが、まずは冒頭でふれた乱数(Ramdomクラス)から解説します。
Rubyの乱数クラス Random
乱数とは、何ら規則性がなく一定の範囲で均一に出る数値のことです。もう少しいうと、都度異なる数値かつ偏っていない(特定の数値ばかり出る確率が高いというのはダメ)数値のことです。
例えば「1から100までの範囲で乱数を発生させる」という場合です。
これを先ほどの解説に当てはめてみると「1つの値を連続して何度も取り出す。ただし、1から100までの間で、規則性がなく、確率的に偏りがないこと」となります。
これをRubyで実装するには、Randomクラスを使います。
puts Random.rand(100)
[実行結果]
※連続して何度も実行しています。
28 59 59 62 39 94
randメソッドの引数に100を与えたので、0から100より小さな整数がランダムに発生しました。引数を与えない場合は、ゼロから1より小さい値が返ります。
Mathモジュール
三角関数や平方根など、数値計算でよく使うものはMathモジュールにて提供されています。
puts Math.sqrt(100) puts Math.sin(Math::PI/2)
[実行結果]
10.0 1.0
結果の1行目は、100の平方根である10が返りました。2行目は、sin90度の1.0が返ってきました。
Mathモジュールには定数も提供されています。Math::PIで円周率πが得られます。それを1/2したからsin90度となったわけです。
Mathモジュールのより詳しい解説は、こちらをごらんください。
Rubyにおいても悩ましい端数処理
どの開発言語でも、端数処理は悩ましい問題です。
例えば、3,300円に消費税を加算した金額を計算しましょう。
puts 3300 * 1.08
[実行結果]
3564.0000000000005
「消費税計算は端数切り上げ」というルールがあると、ceilを使って以下の結果を出せます。
puts (3300 * 1.08).ceil
[実行結果]
3565
目的とする3564円より1円多く出ました!?金額計算でそれは致命的です。
Rubyでは、BigDecimalを使い以下のように対処します。
require "bigdecimal" puts (BigDecimal("3300")*BigDecimal("1.08")).ceil
[実行結果]
3564
今度は正しく計算できました!
でも、なぜBigDecimalに文字列で値を渡さなければいけないのでしょうか?
では試しに普通に数値で渡しましょう。
どこでエラーになったのか分かるように、先ほどの計算をバラして試しましょう。
require "bigdecimal" puts BigDecimal(3300) puts BigDecimal(1.08) puts BigDecimal(3300)*BigDecimal(1.08) puts (BigDecimal(3300)*BigDecimal(1.08)).ceil
[実行結果]
(ファイル名):3:in `BigDecimal': can't omit precision for a Float. (ArgumentError) from 5473.rb:3:in `'
3行目で引数エラーがかかりました。
エラーメッセージを見ると、1.08と書いたとたんにFloat型のオブジェクトが生成されてしまい、それをBigDecimalに渡すと精度が落ちるということでした。
Rubyでの分数の計算 Rational
Rubyには、分数計算をするメソッドがあります。正確にいうと、有理数の計算を行うメソッドです。
有理数とは、分数で表せることができる数のことです。平方根や虚数などは、分数の形で表せないので無理数といいます。
ところで、以下の計算をしてみましょう。0.6と0.7を足すと1.3になりますので、trueが返るはずです。
a = 0.6 b = 0.7 c = 1.3 puts a + b == c
[実行結果]
false
こんな超単純な計算ですら正しい結果が出ないのは、Rubyのせいでしょうか?決してそうではなく、ここには浮動小数点に関する重要なテーマがあります。
かなり大事なことなので、じっくりと解説します。
人間の世界では、0.1は1/10、0.3は3/10と表せます。しかし浮動小数点は1/2、1/4、1/8・・・と2のべき乗の逆数で表現します。
0.5は1/2、0.25は1/4、0.75は1/2 + 1/4・・・と表現できるので問題ありません。では0.3はどうでしょうか?これは2のべき乗の逆数では表現できません。
がんばって0.3を2のべき乗で表現するとすると小数点以下が延々と続き、どこかで計算を打ち切らないといけません。
ここが誤差となって現れるわけです。これを丸め誤差といいます。
これに対処するにはどうすれば良いか、その解決策の1つとしてRationalを使う方法があります。
要は分母が2ではなく10であれば問題ありません。その考えをプログラムに載せましょう。
a = Rational(6,10) # 0.6を意味します b = Rational(7,10) # 0.7を意味します c = Rational(13,10) # 1.3を意味します puts c == a + b
[実行結果]
true
今度は正しく計算できました!
念のためBigDecimalを使って同じことをしてみます。
require "bigdecimal" a = BigDecimal("0.6") # 0.6を意味します b = BigDecimal("0.7") # 0.7を意味します c = BigDecimal("1.3") # 1.3を意味します puts c == a + b
[実行結果]
true
BigDecimalでも正しく計算できました!
まとめ
本記事では、クラスを使った数値計算や端数処理を解説しました。
本記事の内容を自分で何度も試して、効率的かつ正確な数値計算ができるようになってくださいね!