Ruby中的數值

數值類型

Ruby中全部數值都是Numeric類的子類對象,數值都是不可變對象html

數值類型的繼承關係以下:ruby

Integer是整數,Float是浮點數類型,Rational是分數。測試

對於整數,要麼是Fixnum,要麼是Bignum:Fixnum是比較小整數的類型(31個二進制位),Bignum是較大整數的類型。實際上,Ruby中的整數能夠變得任意大。可是浮點數不會任意大,浮點數位數達到一點程度後會溢出到正、負無窮ui

Fixnum和Bignum之間在須要的時候會自動轉換:當一個初始爲Bignum類型的變量值變小後,會自動轉換成Fixnum類型;同理,Fixnum的變量也會在它變得足夠大時轉換成Bignum類型。3d

對於分數,只須要在某個數後面加上一個後綴字母r,就會自動轉換爲分數形式。例如0.3r等價於分數(3/10)2r等價於2/1r等價於分數形式(2/1)。在Kernel模塊中有一個Rational方法,能夠用來將各類類型轉換成分數形式。code

關於算術操做的類型轉換

當算術運算的兩個操做數中,有一個是Float類型,那麼整數類型將轉換成Float類型進行運算,運算結果也都是Float類型htm

>> 2 * 2.5
=> 5.0

>> 1.0 + 2
=> 3.0

>> 5.0/2.5
=> 2.0

>> 5.0/2
=> 2.5

對於整數除法,即兩個數都是整數的除法,那麼除法獲得的結果將是截斷後的整數。對於結果爲負數的整數除法,將取比它精確結果更小的整數。也就是說,Ruby中的整數除法採起的是地板除法(floor)因此,(-a)/b等價於a/(-b),可是可能不等價於-(a/b)對象

>> 5/2
=> 2

>> -3/2    # (-a)/b
=> -2
>> 3/-2    # a/(-b)
=> -2
>> -(3/2)  # -(a/b)
=> -1

浮點數是不精確的,因此不要在浮點數參與運算的時候對浮點數作等值比較。非要比較,能夠經過減法運算,跟一個足夠小的值作大小比較,但也別小過頭了。例如,(0.4-0.1)和0.3作等值比較:blog

>> (0.4 - 0.1) == 0.3
=> false

>> 0.4 - 0.1
=> 0.30000000000000004

>> ( 0.4 - 0.1 ) - 0.3 < 0.00001  # 這是正確的浮點數參與運算的等值比較方式
=> true
>> ( 0.4 - 0.1 ) - 0.3 < 0.0000000000000000000001
=> false

可使用Rational分數來比較:繼承

>> (0.4r-0.1r) == 0.3r     #=> true

>> 0.4r                    #=> (2/5)
>> 0.1r                    #=> (1/10)
>> 0.4r-0.1r               #=> (3/10)
>> 0.3r                    #=> (3/10)

也可使用BigDecimal類來進行運算,它採用的是十進制表示法來表示浮點數,而Float採用的是二進制表示法表示。只不過BigDecimal的運算速度要比正常的浮點數速度慢上不少個數量級,固然,對於普通的財務運算等領域也足夠了,只是在進行科學運算的時候,BigDecimal就不夠了。另外,BigDecimal不是內置類,只是一個標準庫,須要先導入。

require "bigdecimal"
(BigDecimal("0.4") - BigDecimal("0.1")) == BigDecimal("0.3")  #=> true

如下是幾種浮點數運算等值比較的效率高低(比較100W次):

# 直接使用浮點數比較,比較是不精確的
$ time ruby -e '1000000.times {|x| (0.4-0.1) == 0.3 }'

real    0m0.147s
user    0m0.063s
sys     0m0.078s

# 直接使用浮點數作不等值比較,比較是精確的
# (屢次測試,速度比上面要慢一點點,多了次運算)
$ time ruby -e '1000000.times {|x| (0.4-0.1) - 0.3 < 0.00001 }'

real    0m0.158s
user    0m0.094s
sys     0m0.063s

# 使用分數字面量,比較是精確的
$ time ruby -e '1000000.times {|x| (0.4r-0.1r) == 0.3r }'

real    0m0.248s
user    0m0.188s
sys     0m0.094s

# 使用Kernel中的Rational()
$ time ruby -e '1000000.times {|x| (Rational("0.4") - Rational("0.1")) == Rational("0.3") }'

real    0m0.630s
user    0m0.563s
sys     0m0.063s

# 使用bigdecimal
$ time ruby -r"bigdecimal" -e '1000000.times do |x| 
                (BigDecimal("0.4") - BigDecimal("0.1")) == BigDecimal("0.3") 
            end'

real    0m1.079s
user    0m0.953s
sys     0m0.125s

可見,使用分數字面量或浮點數不等值比較的效率是比較可取的,而使用Kernel.Rational()或BigDecimal()的效率相比之下都比較差。

對於Ruby中的取模%運算,也是支持浮點數的。

>> 1.5 % 0.4
=> 0.29999999999999993

指數運算時,採起的是Fortran裏的優先級模式,和Perl是同樣的:從右向左計算。例如3 ** 4 ** 2等價於3 ** (4 ** 2),即其值爲3 ** 16

>> 3 ** 4 ** 2
=> 43046721
>> 3 ** 16
=> 43046721

指數運算的指數支持浮點數、負數,只是指數涉及到整數除法運算時須要注意,由於對於整數除法,Ruby默認採用的是floor除法:

x ** 4
x ** -1      # 即x分之1
x ** (1/3.0) # 即x的立方根
x ** (1/4)   # 1/4=0,等價於x ** 0,即結果爲1
x**(1.0/4.0) # 即x的四次方根

雖然,數值是不可變對象,可是對於整數來講,它支持索引查找各個二進制位上的值,只是不容許修改。

>> printf "%b\n", 12345
11000000111001    # 這是十進制12345對應的二進制數

>> x[0]
=> 1
>> x[1]
=> 0
>> x[4]
=> 1
>> x[5]
=> 1
>> x[6]
=> 0
>> x[11]
=> 0
>> x[12]
=> 1

因此,要判斷一個數是否爲偶數,就很是容易了:

x[0] == 0  # 偶數返回true,奇數返回false
相關文章
相關標籤/搜索