2進数、8進数、16進数(_ヘックス_とも呼ばれます)は、それぞれ基数の異なる記数法です。 2進数は基数2、8進数は基数8、16進数は基数16です。 Crystalでは、ふつうの整数は基数10です。 2進数、8進数、16進数は、どれも整数の表現方法です。 つまり、小数や分数を使わずに正の数と負の数(ゼロを含む)を表し、整数でできるすべての演算をサポートします。
2進数は基数2の記数法で、0と1の数字だけを使います。 コンピューターのトランジスターやスイッチを流れる電気の「オフ」(0)と「オン」(1)の状態や、磁気記憶媒体の正負の電荷をよく表します。 2進数は、基数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を表したい場合、2進数では110になります。
| 位の値 | 4 | 2 | 1 |
|---|---|---|---|
| 2進数の数字 | 1 | 1 | 0 |
計算は4 + 2 + 0 = 6のようになります。
もう1つの例を見てみましょう。19です。
| 位の値 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|
| 2進数の数字 | 1 | 0 | 0 | 1 | 1 |
2進数では10011になります。
計算は16 + 0 + 0 + 2 + 1 = 19のようになります。
Crystalでは、0bプレフィックスを使って2進数のリテラルを表せます。
0b10011と書くと、Crystalはそれを2進数として解釈し、基数10に変換します。
0b10011
# => 19
typeof(0b10011)
# => Int32
2進数ではない数値に0bプレフィックスを付けて書くと、コンパイルエラーになります。
0b10211
# => Error: unexpected token: "211"
2進数は整数なので、整数でできるすべての演算を実行できます。
0b10011 + 0b10011
# => 38
0b10011 * 0b10011
# => 361
0b10011 / 3
# => 6.333333333333333
Crystalは2進数リテラルを自動的にInt32に変換します。
Int32を2進数表現に変換するには、<int>.to_s(base)メソッドを使い、base引数に2を指定します。
このメソッドは、2進数の文字列表現を返します。
19.to_s(2)
# => "10011"
2進数の文字列表現を整数に変換するには、<int>.to_i(base)メソッドを使い、引数に基数(2)を渡します。
"10011".to_i(2)
# => 19
間違った基数(または無効な2進数表現)を渡すと、ArgumentErrorが発生します。
"011021".to_i(2)
# => Invalid Int32: "011021" (ArgumentError)
整数に対して使えるメソッドの中には、その数値の2進数表現に関する情報を得られるものがあります。
<int>.bit_lengthは、その数値を表すのに必要なビット数を返します。
19.bit_length
# => 5
<int>.bit(index)は、右端(最下位ビット)から数えて、指定したインデックスのビットを返します。
19.bit(0)
# => 1
0b10011.bit(1)
# => 1
8進数は基数8の記数法です。 0、1、2、3、4、5、6、7の数字を使います。
Crystalでは、0oプレフィックスを使って8進数を表せます。
2進数の場合と同じく、Crystalは8進数表現を自動的にInt32に変換します。
0o123
# => 83
8進数ではない数値に0oを付けると、コンパイルエラーになります。
2進数の場合と同じく、<int>.to_s(base)メソッドを使ってInt32を8進数表現に変換できます。
また、<int>.to_i(base)メソッドを使って8進数表現をInt32に変換できます。
83.to_s(8)
# => "123"
"123".to_i(8)
# => 83
2進数の場合と同じく、間違った基数を渡すとArgumentErrorが発生します。
16進数は基数16の記数法です。 0〜9の数字と、A、B、C、D、E、Fの文字を使います。 Aは10、Bは11、Cは12、Dは13、Eは14、Fは15です。
Crystalでは、0xプレフィックスを使って16進数を表せます。
2進数や8進数の場合と同じく、Crystalは16進数リテラルを自動的にInt32に変換します。
#=> 0x123
291
16進数ではない数値に0xを付けると、コンパイルエラーになります。
2進数の場合と同じく、<int>.to_s(base)メソッドを使ってInt32を16進数表現に変換できます。
また、<int>.to_i(base)メソッドを使って16進数表現をInt32に変換できます。
291.to_s(16)
# => "123"
"123".to_i(16)
# => 291
2進数や8進数の場合と同じく、間違った基数を渡すとArgumentErrorが発生します。
数値を文字列表現に変換するとき、その数値の精度を指定できます。
これは、その数値を表すために使われる最小の桁数を意味します。
数値の桁数が精度より少ない場合、文字列表現の先頭にゼロが追加されます。
これは、<int>.to_s(<base>, precision: <value>)メソッドにprecision引数を渡すことで行います。
291.to_s(16, precision: 5)
# => "00123"
543.to_s(precision: 2)
# => "543"
友達は、写真編集ソフトにお金を払うのに疲れてしまい、自分で作ることにしました。 友達は、その新しいソフトをFoto Fusionistと名付けることにしました。
Foto Fusionistは、いろいろなことができる写真編集ソフトですが、今のところは、そのごく一部しか実装されていません。 それでも友達はその進み具合に大満足していて、さらにいくつかの機能を実装してほしいと頼んできました。
最初の機能として、友達は、ある数値の2進数表現を整数に変換できるようにしたいと考えています。 これは、ソフトがカラーチャンネルの2進数表現を読み取り、整数に変換できるようにするためです。
String型の引数binaryを取るメソッドFotoFusionist.binary_to_intを実装します。
このメソッドは、その2進数の整数表現を返します。
FotoFusionist.binary_to_int("10011")
# => 19
2つ目の機能は、整数から最下位ビットを取得できるようにすることです。 最下位ビットは、もっとも右にあるビットです。 もっとも小さい値を持つことから、最下位ビットと呼ばれています。
最下位ビットは、カラーチャンネルの表現に使うビット数を減らして画像を最適化できるかどうかを判断するために必要です。
Int32型の引数numberを取るメソッドFotoFusionist.least_significant_bitを実装します。
このメソッドは、その数値の最下位ビットを返します。
FotoFusionist.least_significant_bit(0b10011)
# => 1
FotoFusionist.least_significant_bit(0b10010)
# => 0
3つ目の機能は、カラーチャンネルから16進数の値を取得できるようにすることです。 16進数の値は、カラーチャンネルを16進数表現に変換するために必要で、その16進数表現は2桁でなければなりません。
Int32型の引数numberを取るメソッドFotoFusionist.hex_valueを実装します。
このメソッドは、その数値の16進数の値をStringとして返します。
FotoFusionist.hex_value(19)
# => "13"
4つ目の機能は、RGBカラーから16進数の値を取得できるようにすることです。 これは、ユーザーが色を1つの文字列として表せるようにするために必要です。 RGBカラーとは、赤・緑・青の3つのカラーチャンネルで表現される色です。 各カラーチャンネルは、0から255までの数値で表されます。
RGBカラーの16進数の値は、その色を16進数で表した文字列で、赤のカラーチャンネルが最初、次に緑のカラーチャンネル、最後に青のカラーチャンネルが来ます。
Int32型の3つの引数red、green、blueを取るメソッドFotoFusionist.hex_value_from_rgbを実装します。
このメソッドは、RGBカラーの16進数の値をStringとして返します。
FotoFusionist.hex_value_from_rgb(255, 0, 0)
# => "ff0000"