07 Ruby 數字物件
人們所熟知的數字型別有整數、小數、分數等等,今天讓我們學習在 Ruby 中學習數字物件,瞭解在 Ruby 中數字是如何進行運算的。
1. 為什麼要使用數字物件
自然界的每個事物,我們通常根據其特徵將數字分為不同的集合,開發的時候我們一共能接觸到的數字按照特徵可以分為自然數、整數、有理數、無理數。為了能讓我們對數字進行我們熟知的運算操作(例如:加減乘除),Ruby 使用了數字物件。
2. Ruby 中數字物件
在不同的程式語言中擁有各式各樣的數字型別。在 Ruby 中我們將數字物件分為整型(Integer)
、有理數(Rational)
、浮點數(Float)
、小數(BigDecimal)
四種。
2.1 整型(Integer )
自然數是指從1開始按順序加1的自然數。例如:1、2、3、4、5…。整數是相同的數字,但也包含與此對應的負數以及0,即0,-1,-2,-3,-4,…。Ruby對此集合有一個表示形式:抽象類 Integer。
注意事項:在 Ruby2.4 版本之前,Integer 有 Fixnum 和 Bignum 兩個子類,他們所處理的數字大小範圍不同。
Fixnum 的範圍是在 -2^62 ~ 2^62-1
之間,超出範圍則自動變為 Bignum。
例項:
# Ruby2.4之前
# 以下 > 均代表irb的輸入模式
# => 後面是返回值
> integer = 2 ** 62 - 1
= > 4611686018427387903
> integer.class
=> Fixnum
> integer = (integer + 1).class
=> Bignum
> (-integer - 2).class
=> Bignum
解釋:當前程式碼執行環境是在 Ruby 2.2.4
,我們可以看到 2^62-1 所表示的整數類是 Fixnum,當把 integer 增加 1 後,類名變成了 Bignum。
Ruby 2.4
及以後不會使用 Fixnum 和 Bignum,但是內部它們仍然以相同的方式工作,Ruby 會自動從一種型別切換到另一種型別。意思是較小的 Integer 數字仍然以與 Fixnum 相同的方式執行。
# Ruby2.4及之後的版本
> integer = 2 ** 62 - 1
=> 4611686018427387903
> integer.class
=> Integer
> integer = (integer + 1).class
=> Integer
> (-integer - 2).class
=> Integer
為了能讓 Integer 更好地被讀取,您可以在數字之間增加下劃線,比如 188_000 就會比 188000 更好讀懂。
例項:
> 188_000
=> 188000
對於常見的基本的運算您需要注意除法,當除數與被除數均為 Integer 的時候,返回的結果仍然是 Integer,小數點及後面的值會被省略。
例項:
> 1000000/6
=> 166666
2.2 有理數(Rational)
在現實中,我們不能用整數代表一切。比如整數 1 除以 2,您可以用兩種方式檢視結果,一種是 1/2,另一種是 0.5。而這種類似比率或除法的表現形式,被稱作 有理數(Rational)
。
下面是有理數的建立形式:
例項:
> Rational(1, 2) # 第一個數為分子,第二個數為分母
=> (1/2)
除此之外您還可以使用硬編碼的模式:使用十進位制的數字並在後面加上r
例項:
> 0.5r
=> (1/2)
當有理數之間或者有理數與整數進行運算的時候,得到的結果都是有理數型別。
例項:
> rational = Rational(1, 2) + Rational(1, 2)
=> (1/1)
> rational.class # 檢視這個變數的類
=> Rational
> rational.to_i # to_i意思為轉換成Integer
=> 1
Tips : 如果您對獲取結果的可讀性和精度有極高的要求,而且整型不滿足結果的需求,請使用有理數,
2.3 浮點數(Float)
不是所有的數字都可以使用比例的方式來表示,比如 π
。為了在 Ruby 中表示 無理數(Irrationals)
,我們使用了浮點數(Float)
。
下面舉一個 π 的例子,我們使用 Math::PI
來獲取π。
例項:
> Math::PI
=> 3.141592653589793
> Math::PI.class
Float
我們在 Ruby 中所定義的帶小數點的數字也都是浮點數。
> 1.2.class
=> Float
> 0.00001.class
=> Float
Tips:浮點數在 Ruby 中是不精確的。
例項:
> 0.2 + 0.1 == 0.3
=> false
> 0.2 + 0.1
=> 0.30000000000000004
> (0.2 + 0.1 + 0.7) == 1.0
=> true
您會發現在浮點數的運算中,2.0 - 1.1和0.9並不相等,發生這種情況是因為1985年由IEEE定義的標準(以及 Ruby在其內部使用的標準)以有限的精度儲存數字(這個可以不深究)。如果需要始終正確的十進位制數,則需要使用小數(BigDecimal)。
當 Float 的結果非常大超出了其精度範圍,我們使用 Infinity
。
例項:
> 500.0e1000 # 500.0的1000次方
=> Infinity
超出範圍的計算也是同樣的結果,比如除數為 0 的情況。
例項:
> 1 / 0.0
=> Infinity
> -1 / 0.0
=> -Infinity
Tips:也可以直接使用
Float::INFINITY
來直接呼叫。
為了顯示非數字的結果,Ruby 引入了特殊值 NaN。
例項:
> 0 / 0.0
=> NaN
> Float::INFINITY / Float::INFINITY
=> NaN
> 0 * Float::INFINITY
=> NaN
2.4 小數(BigDecimal)
在 Ruby 中 小數(BIgDecimal)
可以為您提供一個任意精度的十進位制數字。
在使用小數前,我們要引入一個bigdecimal
,定義小數的時候我們要使用一個字串(String)(內容用雙引號或單引號括起來,在Ruby字串物件的章節中會詳細講到)。
> require 'bigdecimal'
=> true
> BigDecimal("0.2") + BigDecimal("0.1") == 0.3
=> true
解釋:為了能使用BigDecimal
方法,要執行require 'bigdecimal'
來引用這個庫。
經驗:在開發中對使用者設定餘額或者金融計算的時候,一定要使用小數,因為這種情況不允許我們出現小數點精度不準確的問題。
注意事項:我們建立 BigDecimal 的時候一定要使用字串作為引數,使用浮點數同樣會造成精度缺失的問題。
既然小數是準確的那為什麼 Ruby 預設的帶小數點的數字是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 為了精確地表達精度,將整數部分和小數部分分開運算,所以花費了很多時間。
3. 常見的數字物件的例項方法
數字物件是一個物件,它擁有很多例項方法,下面會講到一些常見的例項方法,如果是某個型別專用,我會使用括號標記出來。
3.1 基本數學運算
基本數學運算就是我們常見的加(+
)減(-
)乘(*
)除(/
)以及取餘(%
)。
經驗:
-
整型之間進行運算結果返回整型;
-
如果有浮點數參與運算結果返回浮點數;
-
整型的除法會返回商的整數部分。
例項:
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
也可以對算數表示式結果進行判斷。
例項:
1 + 1 == 2 #true
注意事項:
因為浮點數不準確,不建議使用浮點數進行精確的比較。精確的比較請使用 小數(BigDecimal)
。
例項:
5.01 == 5.0 + 1.01 # false
3.3 判斷值與數字型別是否均相等
eql?
方法則可以判斷值和型別是否均相同。
例項:
1 == 1.0 # true
1.eql?(1.0) # false 1是Integer,1.0是Float
3.4 奇偶性的判斷(整型)
odd?
是奇數的判斷,even?
是偶數的判斷。
3.odd? # true
2.even? # true
3.5 小數點位數保留
這裡我們有 3 個方法ceil
、floor
、round
。
-
ceil
返回不小於該數字的最大整數; -
round
返回該數字四捨五入後的整數; -
floor
返回不大於該數字的最大整數。
例項:
2.5.ceil # 3
2.5.round # 3
2.5.floor # 2
我們也可以通過傳遞引數,來調整位數,預設引數為0,往小數點右邊為正,左邊為負。
例項:
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 類別轉換
常用的有to_i
、to_f
、to_s
。
-
to_i
轉換為整型; -
to_f
轉換為浮點型; -
to_s
轉換為字串。
例項:
1.0.to_i # 1
1.to_f # 1.0
1.0.to_s # "1.0"
3.7 最大公因數(整型)
使用 gcd()
,例如:求 10 和 5 的最大公因數。
例項:
10.gcd(5) # 5
3.8 最小公倍數(整型)
使用 lcm()
,例如:取 10 和 5 的最小公倍數。
例項:
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 還可以傳遞第二個引數,意思為在取冪之後再求餘數。
2.pow(10, 100) # 24,相當於 2**10 % 100
3.11 判斷是否為 0
使用zero?
。
例項:
0.zero? # true
4. 小結
本章節我們學習了整型、有理數、浮點數、小數,知道了浮點數在 Ruby 中是不準確的,而小數是準確的,瞭解了常用的數字物件例項方法,例如如何運算、比較、型別轉換等等。在實際專案中,我們會不斷使用到數字物件,一定要好好學習和總結。