Ruby 數(shù)字對象
人們所熟知的數(shù)字類型有整數(shù)、小數(shù)、分數(shù)等等,今天讓我們學習在 Ruby 中學習數(shù)字對象,了解在 Ruby 中數(shù)字是如何進行運算的。
1. 為什么要使用數(shù)字對象
自然界的每個事物,我們通常根據(jù)其特征將數(shù)字分為不同的集合,開發(fā)的時候我們一共能接觸到的數(shù)字按照特征可以分為自然數(shù)、整數(shù)、有理數(shù)、無理數(shù)。為了能讓我們對數(shù)字進行我們熟知的運算操作(例如:加減乘除),Ruby 使用了數(shù)字對象。
2. Ruby 中數(shù)字對象
在不同的編程語言中擁有各式各樣的數(shù)字類型。在 Ruby 中我們將數(shù)字對象分為整型(Integer)
、有理數(shù)(Rational)
、浮點數(shù)(Float)
、小數(shù)(BigDecimal)
四種。
2.1 整型(Integer)
自然數(shù)是指從1開始按順序加1的自然數(shù)。例如:1、2、3、4、5…。整數(shù)是相同的數(shù)字,但也包含與此對應的負數(shù)以及0,即0,-1,-2,-3,-4,…。Ruby對此集合有一個表示形式:抽象類 Integer。
注意事項:在 Ruby2.4 版本之前,Integer 有 Fixnum 和 Bignum 兩個子類,他們所處理的數(shù)字大小范圍不同。
Fixnum 的范圍是在 -2^62 ~ 2^62-1
之間,超出范圍則自動變?yōu)?Bignum。
實例:
# Ruby2.4之前
# 以下 > 均代表irb的輸入模式
# => 后面是返回值
> integer = 2 ** 62 - 1
=> 4611686018427387903
> integer.class
=> Fixnum
> integer = (integer + 1).class
=> Bignum
> (-integer - 2).class
=> Bignum
解釋:當前代碼運行環(huán)境是在 Ruby 2.2.4
,我們可以看到 2^62-1 所表示的整數(shù)類是 Fixnum,當把 integer 增加 1 后,類名變成了 Bignum。
Ruby 2.4
及以后不會使用 Fixnum 和 Bignum,但是內(nèi)部它們?nèi)匀灰韵嗤姆绞焦ぷ?,Ruby 會自動從一種類型切換到另一種類型。意思是較小的 Integer 數(shù)字仍然以與 Fixnum 相同的方式運行。
# Ruby2.4及之后的版本
> integer = 2 ** 62 - 1
=> 4611686018427387903
> integer.class
=> Integer
> integer = (integer + 1).class
=> Integer
> (-integer - 2).class
=> Integer
為了能讓 Integer 更好地被讀取,您可以在數(shù)字之間增加下劃線,比如 188_000 就會比 188000 更好讀懂。
實例:
> 188_000
=> 188000
對于常見的基本的運算您需要注意除法,當除數(shù)與被除數(shù)均為 Integer 的時候,返回的結(jié)果仍然是 Integer,小數(shù)點及后面的值會被省略。
實例:
> 1000000/6
=> 166666
2.2 有理數(shù)(Rational)
在現(xiàn)實中,我們不能用整數(shù)代表一切。比如整數(shù) 1 除以 2,您可以用兩種方式查看結(jié)果,一種是 1/2,另一種是 0.5。而這種類似比率或除法的表現(xiàn)形式,被稱作 有理數(shù)(Rational)
。
下面是有理數(shù)的創(chuàng)建形式:
實例:
> Rational(1, 2) # 第一個數(shù)為分子,第二個數(shù)為分母
=> (1/2)
除此之外您還可以使用硬編碼的模式:使用十進制的數(shù)字并在后面加上r
實例:
> 0.5r
=> (1/2)
當有理數(shù)之間或者有理數(shù)與整數(shù)進行運算的時候,得到的結(jié)果都是有理數(shù)類型。
實例:
> rational = Rational(1, 2) + Rational(1, 2)
=> (1/1)
> rational.class # 查看這個變量的類
=> Rational
> rational.to_i # to_i意思為轉(zhuǎn)換成Integer
=> 1
Tips : 如果您對獲取結(jié)果的可讀性和精度有極高的要求,而且整型不滿足結(jié)果的需求,請使用有理數(shù),
2.3 浮點數(shù)(Float)
不是所有的數(shù)字都可以使用比例的方式來表示,比如 π
。為了在 Ruby 中表示 無理數(shù)(Irrationals)
,我們使用了浮點數(shù)(Float)
。
下面舉一個 π 的例子,我們使用 Math::PI
來獲取π。
實例:
> Math::PI
=> 3.141592653589793
> Math::PI.class
Float
我們在 Ruby 中所定義的帶小數(shù)點的數(shù)字也都是浮點數(shù)。
> 1.2.class
=> Float
> 0.00001.class
=> Float
Tips:浮點數(shù)在 Ruby 中是不精確的。
實例:
> 0.2 + 0.1 == 0.3
=> false
> 0.2 + 0.1
=> 0.30000000000000004
> (0.2 + 0.1 + 0.7) == 1.0
=> true
您會發(fā)現(xiàn)在浮點數(shù)的運算中,2.0 - 1.1和0.9并不相等,發(fā)生這種情況是因為1985年由IEEE定義的標準(以及 Ruby在其內(nèi)部使用的標準)以有限的精度存儲數(shù)字(這個可以不深究)。如果需要始終正確的十進制數(shù),則需要使用小數(shù)(BigDecimal)。
當 Float 的結(jié)果非常大超出了其精度范圍,我們使用 Infinity
。
實例:
> 500.0e1000 # 500.0的1000次方
=> Infinity
超出范圍的計算也是同樣的結(jié)果,比如除數(shù)為 0 的情況。
實例:
> 1 / 0.0
=> Infinity
> -1 / 0.0
=> -Infinity
Tips:也可以直接使用
Float::INFINITY
來直接調(diào)用。
為了顯示非數(shù)字的結(jié)果,Ruby 引入了特殊值 NaN。
實例:
> 0 / 0.0
=> NaN
> Float::INFINITY / Float::INFINITY
=> NaN
> 0 * Float::INFINITY
=> NaN
2.4 小數(shù)(BigDecimal)
在 Ruby 中 小數(shù)(BIgDecimal)
可以為您提供一個任意精度的十進制數(shù)字。
在使用小數(shù)前,我們要引入一個bigdecimal
,定義小數(shù)的時候我們要使用一個字符串(String)(內(nèi)容用雙引號或單引號括起來,在Ruby字符串對象的章節(jié)中會詳細講到)。
> require 'bigdecimal'
=> true
> BigDecimal("0.2") + BigDecimal("0.1") == 0.3
=> true
解釋:為了能使用BigDecimal
方法,要執(zhí)行require 'bigdecimal'
來引用這個庫。
經(jīng)驗:在開發(fā)中對用戶設(shè)置余額或者金融計算的時候,一定要使用小數(shù),因為這種情況不允許我們出現(xiàn)小數(shù)點精度不準確的問題。
注意事項:我們創(chuàng)建 BigDecimal 的時候一定要使用字符串作為參數(shù),使用浮點數(shù)同樣會造成精度缺失的問題。
既然小數(shù)是準確的那為什么 Ruby 默認的帶小數(shù)點的數(shù)字是Float類型呢?
答案是:Float會 比 BigDecimal 快很多,大約快了12 倍。
Calculating -------------------------------------
bigdecimal 21.559k i/100ms
float 79.336k i/100ms
-------------------------------------------------
bigdecimal 311.721k (± 7.4%) i/s - 1.552M
float 3.817M (±11.7%) i/s - 18.803M
Comparison:
float: 3817207.2 i/s
bigdecimal: 311721.2 i/s - 12.25x slower
這是因為 BigDecimal 為了精確地表達精度,將整數(shù)部分和小數(shù)部分分開運算,所以花費了很多時間。
3. 常見的數(shù)字對象的實例方法
數(shù)字對象是一個對象,它擁有很多實例方法,下面會講到一些常見的實例方法,如果是某個類型專用,我會使用括號標記出來。
3.1 基本數(shù)學運算
基本數(shù)學運算就是我們常見的加(+
)減(-
)乘(*
)除(/
)以及取余(%
)。
經(jīng)驗:
-
整型之間進行運算結(jié)果返回整型;
-
如果有浮點數(shù)參與運算結(jié)果返回浮點數(shù);
-
整型的除法會返回商的整數(shù)部分。
實例:
1 + 1 # 2
1 + 1.0 # 2.0
10 / 4 # 2
10 / 4.0 # 2.5
10.0 / 4.0 # 2.5
10 % 3 # 1
3.2 值大小比較
常見的有等于(==
)、不等于(!=
)、大于(>
)、小于(<
)、大于等于(>=
)、小于等于(<=
)。
實例:
1 == 1.0 # true
2 > 1 # true
1 <= 0 # false
也可以對算數(shù)表達式結(jié)果進行判斷。
實例:
1 + 1 == 2 #true
注意事項:
因為浮點數(shù)不準確,不建議使用浮點數(shù)進行精確的比較。精確的比較請使用 小數(shù)(BigDecimal)
。
實例:
5.01 == 5.0 + 1.01 # false
3.3 判斷值與數(shù)字類型是否均相等
eql?
方法則可以判斷值和類型是否均相同。
實例:
1 == 1.0 # true
1.eql?(1.0) # false 1是Integer,1.0是Float
3.4 奇偶性的判斷(整型)
odd?
是奇數(shù)的判斷,even?
是偶數(shù)的判斷。
3.odd? # true
2.even? # true
3.5 小數(shù)點位數(shù)保留
這里我們有 3 個方法ceil
、floor
、round
。
-
ceil
返回不小于該數(shù)字的最大整數(shù); -
round
返回該數(shù)字四舍五入后的整數(shù); -
floor
返回不大于該數(shù)字的最大整數(shù)。
實例:
2.5.ceil # 3
2.5.round # 3
2.5.floor # 2
我們也可以通過傳遞參數(shù),來調(diào)整位數(shù),默認參數(shù)為0,往小數(shù)點右邊為正,左邊為負。
實例:
2.555.ceil(1) # 2.6
2.555.round(1) # 2.6
2.555.floor(1) # 2.5
2.555.ceil(-1) # 10
2.555.round(-1) # 0
2.555.floor(-1) # 0
3.6 類別轉(zhuǎn)換
常用的有to_i
、to_f
、to_s
。
-
to_i
轉(zhuǎn)換為整型; -
to_f
轉(zhuǎn)換為浮點型; -
to_s
轉(zhuǎn)換為字符串。
實例:
1.0.to_i # 1
1.to_f # 1.0
1.0.to_s # "1.0"
3.7 最大公因數(shù)(整型)
使用 gcd()
,例如:求 10 和 5 的最大公因數(shù)。
實例:
10.gcd(5) # 5
3.8 最小公倍數(shù)(整型)
使用 lcm()
,例如:取 10 和 5 的最小公倍數(shù)。
實例:
10.lcm(5) # 10
3.9 絕對值
使用 abs
,例如:取 -1 和 1.0 的絕對值。
-1.abs # 1
1.0.abs # 1.0
3.10 冪
有兩種方式,第一種為**
。
2**10 # 1024
第二種為pow()
。
2.pow(10)
除此之外 pow 還可以傳遞第二個參數(shù),意思為在取冪之后再求余數(shù)。
2.pow(10, 100) # 24,相當于 2**10 % 100
3.11 判斷是否為 0
使用zero?
。
實例:
0.zero? # true
4. 小結(jié)
本章節(jié)我們學習了整型、有理數(shù)、浮點數(shù)、小數(shù),知道了浮點數(shù)在 Ruby 中是不準確的,而小數(shù)是準確的,了解了常用的數(shù)字對象實例方法,例如如何運算、比較、類型轉(zhuǎn)換等等。在實際項目中,我們會不斷使用到數(shù)字對象,一定要好好學習和總結(jié)。