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