LoginSignup
4
2

More than 1 year has passed since last update.

<Ruby>rationalizeメソッドとto_rメソッドの違い

Last updated at Posted at 2021-11-17

はじめに

rationalizeメソッドとto_rメソッド、どちらもレシーバーを有理数に変換したものを返してくれる「同じ」メソッドだと勘違いしてました。(sizeメソッドとlengthメソッドみたいな)
そんな私が、rubyコミュニティーの強い人たちに質問して得た知識をまとめます。

to_rメソッドとrationalizeメソッドとは

レシーバーを有理数に変換します。同じ結果が取得できる時もあります

a = 0.25

a.to_r          ==>(1/4)
a.rationalize   ==>(1/4)

しかし

b = 0.1

b.to_r          ==>(3602879701896397/36028797018963968)
b.rationalize   ==>(1/10)

というふうに結果が異なる時もあります。

両者の決定的な違い

Float#to_r は Float の値と等しい有理数を返します。
一方、Float#rationalize は Float の値を近似する有理数を返します。

では実例を以下で考えていきます。

b = 0.1

b.to_r          ==>(3602879701896397/36028797018963968)
b.rationalize   ==>(1/10)

そもそも、なぜb.to_rの場合、(3602879701896397/36028797018963968)という複雑な数字になってしまうのでしょうか。

それには、"0.1"という文字列をFloatオブジェクトに変換する過程で丸め誤差が起きてしまっていることが関係しています。プログラムのテキストは見た目が数値でも文字列扱いなので、"0.1"という文字列をFloatオブジェクトに変換するというのが、プログラム実行開始より前に発生します。

その際、コンピュータは内部的に2進数を使って計算を行なっているのですが、0.1は2進数では(有限桁数で)表現しきれないため丸め誤差が起こるようです。

そして、丸め誤差を含んだFloatオブジェクトをto_rメソッドで有理数に変換しているので(3602879701896397/36028797018963968)という複雑な数字になっています。

ではなぜrationalizeメソッドの場合は(1/10)というスッキリとした数字なのかというと、rationalizeメソッドはFloatの値に近い有理数を探索してしてくれるためです。(3602879701896397/36028797018963968)に近い(1/10)を探し出して返してくれます。

その他の違い

rationalizeはStringクラスのオブジェクトに対して使用できない

String#to_rは存在しますが、String#rationalizeは存在しません。

"0.25".to_r        ==>(1/4)

"0.25".rationalize  ==>undefined method `rationalize' for "0.25":String 

rationalizeメソッドが使えるオブジェクトはIntegerクラス、Floatクラス、NilClassクラスのオブジェクトのみです。

ですので、例えば競プロで入力を受け取る時

arr = gets.chomp.split(" ").map(&:rationalize)

とはできないので注意です。

4
2
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
4
2