【Ruby】数値クラス

「はじめてのRuby」を読んでのメモ。

今回は第12章「数値(Numeric)クラス」



Numeric クラスの次の機能について、少し掘り下げてみる。

  • Numeric のクラス構成
  • 数値のリテラル
  • 算術演算
  • 型の変換
  • ビット演算
  • 乱数
  • 数え上げ


Numeric のクラス構成

数値クラスは次のように構成されている。


プログラム中に整数が Fixnum の大きさを越えてしまっても自動的に Bignum に変換されるため、整数クラスの違いを気にしなければならないケースはほとんどない。


Rational オブジェクトは「Rational(分子, 分母)」の形式で生成する。

a = Rational(1,2)
b = Rational(1,4)
p a       #=> (1/2)
p b       #=> (1/4)
c = a + b
p c       #=> (3/4)
p c.to_f  #=> 0.75



Complex オブジェクトは「Complex(実数部, 虚数部)」の形式で生成する。

c = Complex(0,2) ** 2
p c    #=> (-4+0i)


数値のリテラル

数値オブジェクトを表現するリテラルは次のようになる。

リテラル 意味(括弧内は10進数での値)
123 整数の10進表記(123)
0123 整数の8新表記(83)
0o123 整数の8進表記(83)
0d123 整数の10進表記(123)
0x123 整数の16進表記(291)
0b1111011 整数の2進表記(123)
123.45 浮動小数点数
1.23e4 浮動小数点数の指数表記(1.23 * 10**4 = 12300.0)
1.23e-4 浮動小数点数の指数表記(1.23 * 10**-4 = 0.000123)



数値リテラル内の「 _ 」は無視される。3桁区切り表現をするのに便利。

p 1234567    #=> 1234567
p 1_234_567  #=> 1234567


算術演算

算術オブジェクト同士の基本的な計算は次の演算子を使って行える。

演算子 演算
+ 加算
- 減算
* 乗算
/ 除算
% 剰余
** べき乗



Integer オブジェクトと Float オブジェクトを計算した結果は Float オブジェクトになる。

p 1 + 1      #=> 2 
p 1.0 + 1.0  #=> 2.0
p 1 + 1.0    #=> 2.0

負の整数でのべき乗は Rational オブジェクトを返す。

p 5 ** -2.0  #=> 0.04
p 5 ** -2    #=> (1/25)


割り算

数値オブジェクトには / と % 以外にも割り算に関するメソッドが存在する。

  • x.div(y) ... x を y で割った商を整数で返す。
p 5.div(2)     #=> 2
p 5.div(2.2)   #=> 2
p -5.div(2)    #=> -3
p -5.div(2.2)  #=> -3
  • x.quo(y) ... x を y を割った商を返す。
p 5.quo(2)     #=> (5/2)
p 5.quo(2.0)   #=> (5/2)
p 5.quo(2.2)   #=> 2.2727272727272725
p -5.quo(2)    #=> (-5/2)
p -5.quo(2.2)  #=> -2.2727272727272725
  • x.modulo(y) ... x を y で割った余りを返す。x % y と同じ。
p 10.modulo(3.5)    #=> 3.0
p 10.modulo(-3.5)   #=> -0.5
p -10.modulo(3.5)   #=> 0.5
p -10.modulo(-3.5)  #=> -3.0
  • x.remainder(y) ... modulo と基本的には同じだが、こちらは結果の符号が「x」の符号に一致する。
p 10.remainder(3.5)    #=> 3.0
p 10.remainder(-3.5)   #=> 3.0
p -10.remainder(3.5)   #=> -3.0
p -10.remainder(-3.5)  #=> -3.0
  • x.divmod(y) ... x を y で割った時の商と剰余を配列にして返す。商は「x / y」の結果を小さい方向に丸めた値になる。
p 10.divmod(2)     #=> [5, 0]
p 10.divmod(3)     #=> [3, 1]
p 10.divmod(3.5)   #=> [2, 3.0]
p 10.divmod(-3.5)  #=> [-3, -0.5]
p -10.divmod(3.5)  #=> [-3, 0.5]
p -10.divmod(-3.5) #=> [2, -3.0]


0による割り算は Integer クラスではエラーとなるが、Float クラスでは違う結果となって返ってくる。

p 1 / 0          #=> Error (ZeroDivisionError)
p 1 / 0.0        #=> Infinity
p 0 / 0.0        #=> NaN
p 1.divmod(0)    #=> Error (ZeroDivisionError)
p 1.divmod(0.0)  #=> Error (FlaotDomainError)

Infinity と NaN はどんな演算をしても、Infinity か NaN にしかならない。

p 1 / 0.0 + 1  #=> Infinity
p 0 / 0.0 * 3  #=> NaN


Math モジュール

三角関数や対角関数など、よく使う数値演算のためのメソッドは Math モジュールで提供されている。

# 平方根
p Math.sqrt(2)  #=> 1.4142135623730951



Math モジュールで提供されるメソッドは次のとおりである。

メソッド名 意味
acos(x) 余弦関数
acosh(x) 双曲線逆余弦関数
asin(x) 逆正弦関数
asinh(x) 双曲線逆正弦関数
atan(x) 正接関数
atan2(x,y) 4象限表現の逆正接関数
atanh(x) 双曲線逆正接関数
cbrt(x) 立方根
cos(x) 余弦関数
cosh(x) 双曲線余弦関数
erf(x) 誤差関数
erfc(x) 相補誤差関数
exp(x) 指数関数
frexp(x) 浮動小数点数正規化小数と指数
gamma(x) ガンマ関数
hypot(x,y) ユークリッド距離関数
ldexp(x,y) 浮動小数点数と2の整数状の積
lgamma(x) ガンマ関数の自然対数
log(x) 底を e とする対数(自然対数)
log10(x) 底を10とする対数(常用対数)
log2(x) 底を2とする対数
sin(x) 正弦関数
sinh(x) 双曲線正弦関数
sqrt(x) 平方根
tan(x) 正接関数
tanh(x) 双曲線正接関数

さっぱりである。


他に、定数 PI と E が用意されている。

定数名 意味
PI 円周率
E 自然対数の底 e


数値型の変換

to_○○ メソッドで型の変換を行える。

p 10.to_f     #=> 10.0
p 10.8.to_i   #=> 10
p -10.8.to_i  #=> -10
p "123".to_i  #=> 123
p 1.5.to_r    #=> (3/2)
p 1.5.to_c    #=> (1.5+oi)

to_i メソッドは小数点以下を切り捨てた値を返す。四捨五入したい場合は round メソッドを使う。

p 10.8.round  #=> 11
p -1.8.round  #=> -2

次のようなメソッドもある。

# レシーバよりも大きい最も小さい整数を返す
p 1.5.ceil   #=> 2
p -1.5.ceil  #=> -1

# レシーバよりも小さい最も大きい整数を返す
p 1.5.floor   #=> 1
p -1.5.floor  #=> -2


ビット演算

Integer クラスでは次のようなビット演算を利用できる。

def pb(i)
  # printfの %b フォーマットを使って
  # 整数の末尾8ビットを2進数表示する
  printf("%08b\n", i & 0b11111111)
end

b = 0b11110000
pb(b)               #=> 11110000

# ビット反転
pb(~b)              #=> 00001111
# ビット積
pb(b & 0b00010001)  #=> 00010000
# ビット和
pb(b | 0b00010001)  #=> 11110001
# 排他的論理和
pb(b ^ 0b00010001)  #=> 11100001
# 右ビットシフト 
pb(b >> 3)          #=> 00011110
# 左ビットシフト
pb(b << 3)          #=> 10000000


乱数

乱数を得るには Random.rand メソッドを使う。

p Random.rand        # 1未満の浮動小数点を返す
p Random.rand(100)   # 0からその値より小さい範囲の数値を返す
p Random.rand(-100)  # Argument Error

引数に負の数を与えることは出来ないらしい。

ソフトウェアでは本物の乱数を作れないので計算によって乱数のように見える値を作る。この乱数を擬似乱数という。擬似乱数では乱数を生成するきっかけとなる値(乱数の種)が必要である。擬似乱数はあくまで計算によって求めているので、この乱数の種があれば得られる値を再現することができる。

Random オブジェクトに乱数の種を指定して乱数を得るには、Random.new メソッドで乱数生成器を初期化し、Random#rand メソッドを使う。

p Random.rand        # 1未満の浮動小数点を返す
p Random.rand(100)   # 0からその値より小さい範囲の数値を返す
#p Random.rand(-100)  # Argument Error

r1 = Random.new(1)
p [r1.rand, r1.rand]
  #=> [0.417022004702574, 0.7203244934421581]
r2 = Random.new(1)
p [r2.rand, r2.rand]
  #=> [0.417022004702574, 0.7203244934421581]
# 同じ値が返される

Random.new メソッドに引数を与えない場合は別の乱数によって作られた種によって Random オブジェクトが初期化されるため、毎回異なる乱数列を得られる。

r1 = Random.new
p [r1.rand, r1.rand]
  #=> [0.026216275101293318, 0.5299758801083105]
r2 = Random.new
p [r2.rand, r2.rand]
  #=> [0.08460756345140141, 0.8883369147389791] 
p [r2.rand, r2.rand]
  #=> [0.013714659866206014, 0.9827272186983755]

もっとも、普通は Random.rand メソッドを用いれば十分である。

数えあげ

Intger クラスは数値の計算の他に、処理の回数や配列の要素数を数えあげるために使われる。以下は数によって指定された分だけ処理を繰り返すイテレータである。

  • n.times{|i| ... }
# n 回の繰り返しを行う
ary = []
10.times do |i|
  ary << i
end
p ary  #=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • from.upto(to){|i| ... }
# from から to に達するまで iを1ずつ加算しながら繰り返し
ary = []
2.upto(10) do |i|
  ary << i
end
p ary  #=> [2, 3, 4, 5, 6, 7, 8, 9, 10]
  • from.downto(to{|i| ... }
# from から to に達するまで i を1ずつ減算しながら繰り返し
ary = []
10.downto(2) do |i|
  ary << i
end
p ary  #=> [10, 9, 8, 7, 6, 5, 4, 3, 2]
  • from.step(to, step){|i| ... }
# from から to に達するまで i に step を足しながら繰り返し
ary = []
2.step(10,3) do |i|
  ary << i
end
p ary  #=> [2, 5, 8]

ary = []
10.step(2,-3) do |i|
  ary << i
end
p ary  #=> [10, 7, 4]



これらのメソッドは、ブロックを与えなければ Enumerator オブジェクトを返す。これにより、step メソッドのブロック変数として得られる一連の数値を Enumerator#collect メソッドで収集できる。

ary = 2.step(10)
p ary  #=> #<Enumerator: 2:step(10)>

ary = 2.step(10).collect{|i| i * 2}
p ary  #=> [4, 6, 8, 10, 12, 14, 16, 18, 20]