二進位、八進位和十六進位(也稱為 hex)是基數不同的進位系統。 二進位的基數是 2,八進位是 8,十六進位是 16。 在 Crystal 中,一般的整數採用基數 10。 二進位、八進位和十六進位都是整數的表示法。 這表示它們可以表示正數和負數(包括零),不帶分數或小數,並且支援我們對整數能做的所有運算。
二進位是基數為 2 的進位系統,只使用數字 0 和 1。 它通常用來表示電腦中電流通過電晶體和開關時的 0(「關」)與 1(「開」)狀態,以及磁性儲存媒體中的正負電荷。 二進位可以表示基數 10 中使用到的所有整數。
基數 2 系統的一小部分看起來像這樣,不過它會無限延伸,不會停在 128:
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|---|---|
| 2 ** 7 | 2 ** 6 | 2 ** 5 | 2 ** 4 | 2 ** 3 | 2 ** 2 | 2 ** 1 | 2 ** 0 |
所以如果我們想表示數字 6,用二進位表示會是:110
| 位值 | 4 | 2 | 1 |
|---|---|---|---|
| 二進位數字 | 1 | 1 | 0 |
而運算會是:4 + 2 + 0 = 6
另一個例子:19
| 位值 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|
| 二進位數字 | 1 | 0 | 0 | 1 | 1 |
這個二進位數字會是:10011
而運算會是:16 + 0 + 0 + 2 + 1 = 19
在 Crystal 中,我們可以使用0b前綴來表示二進位字面值。
如果我們寫0b10011,Crystal 會將它解讀為二進位數字,並轉換成基數 10。
0b10011
# => 19
typeof(0b10011)
# => Int32
如果你寫了一個帶有0b前綴、卻不屬於二進位系統的數字,就會拋出編譯錯誤。
0b10211
# => Error: unexpected token: "211"
由於二進位數字就是整數,我們可以對它們執行所有能用於整數的運算。
0b10011 + 0b10011
# => 38
0b10011 * 0b10011
# => 361
0b10011 / 3
# => 6.333333333333333
Crystal 會自動將二進位字面值轉換成Int32。
若要將Int32轉換成二進位表示法,請使用<int>.to_s(base)方法,並將 base 引數設為 2。
這個方法會回傳該二進位數字的字串表示法:
19.to_s(2)
# => "10011"
若要將二進位的字串表示法轉換成整數,我們可以使用<int>.to_i(base)方法,並將基數(2)作為引數傳入:
"10011".to_i(2)
# => 19
給錯基數(或無效的二進位表示法)會拋出ArgumentError:
"011021".to_i(2)
# => Invalid Int32: "011021" (ArgumentError)
有些方法可以用在整數上,取得該數字二進位表示法的相關資訊。
<int>.bit_length會回傳表示該數字所需的位元數:
19.bit_length
# => 5
<int>.bit(index)會回傳指定索引位置的位元,從右邊(最低有效位元)開始算起:
19.bit(0)
# => 1
0b10011.bit(1)
# => 1
八進位是基數為 8 的進位系統。 它使用數字 0、1、2、3、4、5、6 和 7。
在 Crystal 中,我們可以使用0o前綴來表示八進位數字。
和二進位一樣,Crystal 會自動將八進位表示法轉換成Int32。
0o123
# => 83
在不是八進位系統的數字前加上0o前綴,會拋出編譯錯誤。
和二進位一樣,我們可以使用<int>.to_s(base)方法,將Int32轉換成八進位表示法。
也可以使用<int>.to_i(base)方法,將八進位表示法轉換成Int32。
83.to_s(8)
# => "123"
"123".to_i(8)
# => 83
和二進位一樣,給錯基數會拋出ArgumentError。
十六進位是基數為 16 的進位系統。 它使用數字 0 到 9,以及字母 A、B、C、D、E 和 F。 A 是 10,B 是 11,C 是 12,D 是 13,E 是 14,F 是 15。
我們可以在 Crystal 中使用0x前綴來表示十六進位數字。
和二進位、八進位一樣,Crystal 會自動將十六進位字面值轉換成Int32。
#=> 0x123
291
在非十六進位的數字前加上0x前綴,會拋出編譯錯誤。
和二進位一樣,我們可以使用<int>.to_s(base)方法,將Int32轉換成十六進位表示法。
也可以使用<int>.to_i(base)方法,將十六進位表示法轉換成Int32。
291.to_s(16)
# => "123"
"123".to_i(16)
# => 291
和二進位、八進位一樣,給錯基數會拋出ArgumentError。
將數字轉換成字串表示法時,我們可以指定該數字的精確度。
意思是用來表示該數字的最少位數。
如果該數字的位數少於精確度,字串表示法前面會補上零。
這是透過將precision引數傳給<int>.to_s(<base>, precision: <value>)方法來完成。
291.to_s(16, precision: 5)
# => "00123"
543.to_s(precision: 2)
# => "543"
你的朋友已經受夠了花錢買照片編輯軟體,決定自己寫一套。你的朋友決定把這套新軟體叫做 Foto Fusionist。
Foto Fusionist 是一套能做好多事的照片編輯軟體,不過到目前為止你只實作了其中幾項。不過,你的朋友對目前的進展很滿意,並請你再實作幾項功能。
第一個功能,你的朋友希望能把數字的二進位表示法轉換成整數。這麼做是因為軟體要能讀取某個色彩通道的二進位表示法,並把它轉換成整數。
請實作方法 FotoFusionist.binary_to_int,它接受一個型別為 String 的引數 binary。這個方法應該回傳該二進位數字的整數表示法。
FotoFusionist.binary_to_int("10011")
# => 19
你的朋友想要的第二個功能,是能從整數取得最低有效位。最低有效位是最靠右邊的位元。它被叫做最低有效位,是因為它的值最小。
我們需要最低有效位,來判斷能否藉由減少用來表示色彩通道的位元數,來最佳化一張圖片。
請實作方法 FotoFusionist.least_significant_bit,它接受一個型別為 Int32 的引數 number。這個方法應該回傳該數字的最低有效位。
FotoFusionist.least_significant_bit(0b10011)
# => 1
FotoFusionist.least_significant_bit(0b10010)
# => 0
你的朋友想要的第三個功能,是能從色彩通道取得十六進位值。我們需要十六進位值,才能把色彩通道轉成十六進位表示法,而且給定的十六進位表示法必須由兩位數組成。
請實作方法 FotoFusionist.hex_value,它接受一個型別為 Int32 的引數 number。這個方法應該把該數字的十六進位值以 String 回傳。
FotoFusionist.hex_value(19)
# => "13"
你的朋友想要的第四個功能,是能從 RGB 色彩取得十六進位值。這是為了讓使用者能取得顏色的單一字串表示法。RGB 色彩是由三個色彩通道表示的顏色:紅色、綠色和藍色。每個色彩通道都由一個 0 到 255 之間的數字表示。
RGB 色彩的十六進位值,就是該顏色以十六進位表示的字串,其中紅色通道在最前面,接著是綠色通道,最後是藍色通道。
請實作方法 FotoFusionist.hex_value_from_rgb,它接受三個引數:型別為 Int32 的 red、green 和 blue。這個方法應該把該 RGB 色彩的十六進位值以 String 回傳。
FotoFusionist.hex_value_from_rgb(255, 0, 0)
# => "ff0000"