在Ruby中,數值都是Numeric的子類別實例,Numeric的子類別有Integer代表整數、Float代表浮點數,Integer又有代表固定長度整數的Fixnum與超長整數的Bignum。
Numeric
|-Integer
| |-Fixnum
| |-Bignum
|-Float
|-Integer
| |-Fixnum
| |-Bignum
|-Float
在Ruby中,所有的資料都是物件,但可以使用實字(Literal)方式來撰寫表示數值,直接寫下一個整數值,預設是十進位整數,如果要撰寫二進位實字,則在數字前前0b,如果要撰寫八進位實字,則在數字前置0,之後接上1到7的數字,如果要撰寫十六進位整數,則以0x開頭,之後接上1到9、A到F。例如:
>> 10
=> 10
>> 0b10
=> 2
>> 010
=> 8
>> 0x10
=> 16
>>
=> 10
>> 0b10
=> 2
>> 010
=> 8
>> 0x10
=> 16
>>
Fixnum可容納的長度依機器而有所不同,如果撰寫的整數超出Fixnum可容納的長度,會自動轉換為Bignum,所以基本上不用特別在意兩者的差別:
>> 1.class
=> Fixnum
>> 1111111111111111111111111111111111111111111111111111111111111.class
=> Bignum
>>
=> Fixnum
>> 1111111111111111111111111111111111111111111111111111111111111.class
=> Bignum
>>
可以使用字串、浮點數的to_i方法建立整數,如果是字串,to_i可以指定以二進位、八進位或十六進位將字串代表的數值傳回,事實上,你可以在呼叫to_i時,指定2到36作為基底。。例如:
>> "10".to_i
=> 10
>> 3.14.to_i
=> 3
>> "10".to_i(2)
=> 2
>> "10".to_i(8)
=> 8
>> "10".to_i(16)
=> 16
>> "10".to_i(17)
=> 17
>>
=> 10
>> 3.14.to_i
=> 3
>> "10".to_i(2)
=> 2
>> "10".to_i(8)
=> 8
>> "10".to_i(16)
=> 16
>> "10".to_i(17)
=> 17
>>
兩個整數相除,結果仍會是整數,如果想要有浮點數結果,則其中一個運算元必須是浮點數。例如:
>> 10 / 3
=> 3
>> 10.0 / 3
=> 3.33333333333333
>> 10 % 3
=> 1
>>
=> 3
>> 10.0 / 3
=> 3.33333333333333
>> 10 % 3
=> 1
>>
在上例中,/是除法操作,%則是取得除法後的餘數。除了/與%之外,+、-、*等都可以運用在數值上,另外還有**次方運算。例如要計算2的100次方:
>> 2 ** 100
=> 1267650600228229401496703205376
>>
=> 1267650600228229401496703205376
>>
可以直接寫下3.14這樣的數值作為浮點數,它們會是Float實例,可以使用3.14e-10這樣的表示法。例如:
>> 3.14
=> 3.14
>> 3.14e-10.class
=> Float
=> 3.14
>> 3.14e-10.class
=> Float
類似地,也可以使用整數、字串的to_f方法建立浮點數。例如:
>> "3.14159".to_f
=> 3.14159
>> "2.1e-10".to_f
=> 2.1e-010
>>
=> 3.14159
>> "2.1e-10".to_f
=> 2.1e-010
>>
使用浮點數,同樣要注意浮點數精度問題,例如:
>> 1.0 - 0.8
=> 0.19999999999999996
>> 7.3 - 7.2 == 0.1
=> false
>> 7.3 - 7.2
=> 0.0999999999999996
>> 1.2 - 1.0 - 0.2 == 0.0
=> false
>> 1.2 - 1.0 - 0.2
=> -5.55111512312578e-017
>>
=> 0.19999999999999996
>> 7.3 - 7.2 == 0.1
=> false
>> 7.3 - 7.2
=> 0.0999999999999996
>> 1.2 - 1.0 - 0.2 == 0.0
=> false
>> 1.2 - 1.0 - 0.2
=> -5.55111512312578e-017
>>
開發人員基本上都要了解CPU處理浮點數的設計原理,如果你要精確的結果,那麼可以使用bigdecimal中的BigDecimal類別。例如:
>> require "bigdecimal"
=> true
>> a = BigDecimal.new("7.3")
=> #<BigDecimal:22bde60,'0.73E1',8(8)>
>> b = BigDecimal.new("7.2")
=> #<BigDecimal:2173c20,'0.72E1',8(8)>
>> r = BigDecimal.new("0.1")
=> #<BigDecimal:22548c0,'0.1E0',4(8)>
>> a - b == r
=> true
>> (a - b).to_f
=> 0.1
>>
=> true
>> a = BigDecimal.new("7.3")
=> #<BigDecimal:22bde60,'0.73E1',8(8)>
>> b = BigDecimal.new("7.2")
=> #<BigDecimal:2173c20,'0.72E1',8(8)>
>> r = BigDecimal.new("0.1")
=> #<BigDecimal:22548c0,'0.1E0',4(8)>
>> a - b == r
=> true
>> (a - b).to_f
=> 0.1
>>
這解決了問題,但寫法較為冗長,可以要求bigdecimal中的util特性,這會為Float增加to_d方法,可以直接取得BigDecimal實例。例如:
>> require "bigdecimal/util"
=> true
>> 1.2.to_d
=> #<BigDecimal:2424d20,'0.12E1',8(8)>
>> 1.2.to_d - 1.0.to_d - 0.2.to_d == 0.0.to_d
=> true
>> (1.2.to_d - 1.0.to_d - 0.2.to_d).to_f
=> 0.0
>>
=> true
>> 1.2.to_d
=> #<BigDecimal:2424d20,'0.12E1',8(8)>
>> 1.2.to_d - 1.0.to_d - 0.2.to_d == 0.0.to_d
=> true
>> (1.2.to_d - 1.0.to_d - 0.2.to_d).to_f
=> 0.0
>>