MathSwap関数の働き・役割
MathSwap関数は、指定された数値のバイト順を変更するための関数です。一般的に、エンディアン(バイトの並び順)の変換が必要な場合に使用されます。
エンディアンは、データがどのようにバイト順でメモリに格納されるかを決定します。たとえば、ビッグエンディアン形式では最上位バイトが最初に格納され、リトルエンディアン形式では最下位バイトが最初に格納されます。
MathSwap関数は、ushort型、uint型、およびulong型の数値に対してそれぞれオーバーロードされています。それぞれの関数は、指定されたデータ型のバイト順を逆にして返します。
この関数を使用することで、異なるエンディアンを持つシステム間でデータを交換する際に、バイト順を簡単に変換できます。
エンディアンについて
プログラミングやデータ通信では、「エンディアン」という用語が使われますが、これは「バイトの並び順」を指しています。エンディアンを理解するためには、まず「バイト」とは何か、「バイトの並び順」とは何かを理解する必要があります。
バイトとは?
コンピュータのデータは、基本的に「0」と「1」の数字で表される「ビット」と呼ばれる最小単位でできています。8つのビットが集まると「1バイト」になります。たとえば、次のような「0」と「1」が並ぶと1バイトになります。
01010101
1バイトには256通り(0から255まで)の値が表現でき、アルファベット1文字や小さな数字の情報を表すのに使われます。
バイトを並べるとは?
コンピュータでは、文字や数値などのデータを扱うときに、複数のバイトを並べてデータを表現します。たとえば、数値「300」を2バイトで表現するとします。バイトの並びは以下のようになります。
00000001 00101100
このように複数のバイトが連続して並んだとき、その順番が「バイトの並び順」となります。
数値「300」を2バイトで表現するときの「00000001 00101100」という表記が、10進数の「300」に相当する理由について説明します。
ビットとバイトの構造
まず、「00000001 00101100」は、2バイト(つまり16ビット)で構成されています。この2バイトを左側から見ると、最初の8ビットは「00000001」、次の8ビットは「00101100」となっています。
二進数から10進数への変換方法
このようなバイナリ(2進数)の表記を10進数に変換するには、各ビットに対応する2の累乗を掛け合わせて合計します。たとえば、8ビットの構造の各桁の位置に、以下のように2の累乗を割り当てます:
桁位置 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
値 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
これを2回分、すなわち2バイト分行います。今回は、「00000001」と「00101100」をそれぞれ変換し、足し合わせることで「300」を得ます。
計算手順
00000001 = 1 × 256 = 256
- 2の4乗 (16) の位置に「1」
- 2の3乗 (8) の位置に「1」
- 2の2乗 (4) の位置に「1」 各位置の値を合計すると以下のようになります:
00101100 = 1 × 16 + 1 × 8 + 1 × 4 = 28
256 + 28 = 300
このように、2進数「00000001 00101100」は10進数で「300」を表しています。
エンディアンとは?
エンディアンは、複数のバイトを並べる順番のことを指します。コンピュータはデータをメモリに保存する際に、異なるバイト順で保存することがあります。エンディアンには、主に「ビッグエンディアン」と「リトルエンディアン」という2つの種類があります。
ビッグエンディアン
ビッグエンディアンでは、一番大きい位のバイト(上位バイト)を先に並べます。たとえば、数値「300」をビッグエンディアンで保存すると次のようになります。
00000001 00101100
上位バイトから順に保存されるため、人間が読むのと同じ感覚で並んでいます。
リトルエンディアン
リトルエンディアンでは、一番小さい位のバイト(下位バイト)を先に並べます。同じ数値「300」をリトルエンディアンで保存すると次のようになります。
00101100 00000001
この場合、下位バイトから保存されているため、ビッグエンディアンとは逆の順番で並んでいます。
なぜエンディアンが必要なのか?
エンディアンが異なると、データをそのまま別のシステムで使おうとしたときに正しい値として認識されないことがあります。たとえば、ビッグエンディアンのシステムで保存したデータをリトルエンディアンンのシステムで読み込むと、値が正しく読み取れません。
これを解決するために、バイトの順番を入れ替える必要があり、このときに役立つのがMathSwap関数です。MathSwap関数を使うと、バイト順を逆にしてくれるので、異なるエンディアンのシステム間でデータのやり取りがスムーズになります。
MathSwap関数の引数について
MathSwap関数には、引数として変換対象の値を指定します。この関数は異なるデータ型に対応するため、3つの異なる書式があります。
書式1:ushort型
ushort型の数値のバイト順を反転させるMathSwap関数の書式は以下のとおりです。
ushort MathSwap(
ushort value // 値
);
MathSwap関数は次の引数を持ちます。
書式2:uint型
uint型の数値のバイト順を反転させるMathSwap関数の書式は以下のとおりです。
uint MathSwap(
uint value // 値
);
書式3:ulong型
ulong型の数値のバイト順を反転させるMathSwap関数の書式は以下のとおりです。
ulong MathSwap(
ulong value // 値
);
- value:ulong型の値。バイト順を反転させたい対象の数値を指定します。ulong型は符号なしの64ビット(8バイト)整数で、0から18446744073709551615までの範囲の数値を表現できます。
引数valueの詳細
いずれの書式でも、引数valueはバイト順を反転する対象の数値を指定するために使用します。
MathSwap関数の戻り値について
MathSwap関数は、指定した値のバイト順を反転した結果を返します。それぞれの書式に応じた戻り値の型は、入力した値の型に対応しています。
書式1:ushort型の戻り値
ushort型のMathSwap関数は、バイト順が反転されたushort型の値を返します。もともとushort型は16ビット(2バイト)で表されているため、バイト順を反転することで、元の数値とは異なる値が返されます。例えば、「00000001 00101100」のようなバイト列は「00101100 00000001」に反転されて戻り値として返されます。
書式2:uint型の戻り値
uint型のMathSwap関数は、バイト順が反転されたuint型の値を返します。uint型は32ビット(4バイト)のため、4バイトの各位置が逆順に並び替えられた値が戻り値となります。たとえば、「00000000 00000000 00000001 00101100」というバイト列は、「00101100 00000001 00000000 00000000」に反転されて返されます。
書式3:ulong型の戻り値
ulong型のMathSwap関数は、バイト順が反転されたulong型の値を返します。ulong型は64ビット(8バイト)のため、8バイトの各位置が逆順に並び替えられた値が戻り値となります。たとえば、「00000000 00000000 00000000 00000000 00000001 00101100 00000000 00000001」というバイト列は、「00000001 00000000 00101100 00000001 00000000 00000000 00000000 00000000」に反転されて返されます。
戻り値の詳細
それぞれの書式で、戻り値はバイト順が反転された新しい値です。この変換により、元のデータが異なるエンディアン形式で表現されるようになります。
バイトの並び替えについての補足
、エンディアン変換によってメモリ上の並び順が変わると、同じビットの配列でも異なる数値として解釈されることになります。例えば、2バイト(16ビット)で「00000001 00101100」というバイトの並びを、バイト順を逆にして「00101100 00000001」にすると、元の数値は10進数の「300」でしたが、並び替えた結果は10進数で「768」になります。
このように、バイトの並び順が異なるだけで、全く別の数値に変換されることがあります。
ただ、バイトの並び替えは主にコンピュータ内部でのデータの扱い方の問題であり、10進数表現の変化は「人間が読み取るときにどう解釈するか」という観点で発生します。
コンピュータ内部ではデータは「0」と「1」のビットで処理されており、バイトの並び順が異なることでメモリに保存されるデータの配置が変わります。
しかし、コンピュータはそのビット配列を設定されたエンディアンに従って解釈するため、エンディアンが異なるコンピュータ同士でデータをやり取りする際に、データの正確な読み取りが重要になります。
つまり、バイトの並び替えが必要なのは、異なるエンディアン形式を持つシステム間でのデータ共有や保存の際に、一貫した数値として解釈するためです。
エンディアン変換を行わないと、同じビットパターンが異なる10進数に見える可能性がありますが、これはあくまで「解釈の違い」であり、実際のデータ自体が変化するわけではありません。
まとめると、バイト並び替えはコンピュータ間でのデータの正確なやり取りを可能にするための技術的な問題であり、人間の10進数表現の違いは、その結果として現れる「見かけ上の変化」ということになります。
MathSwap関数を使ったサンプルコード
#property script_show_inputs
// ulong型の入力値、任意の数値を設定できる
input ulong InpLongValue = 1;
// uint型の入力値、任意の数値を設定できる
input uint InpIntValue = 2;
// ushort型の入力値、任意の数値を設定できる
input ushort InpShortValue = 3;
//+------------------------------------------------------------------+
//| スクリプトプログラムの開始関数 |
//+------------------------------------------------------------------+
void OnStart()
{
// 入力値をMathSwap関数でバイト順を反転し、
// 元の数値と変換後の数値を10進数および2進数表現でエキスパートログに出力する
Print(ValueDescription(InpLongValue)); // ulong型の値について出力
Print(ValueDescription(InpIntValue)); // uint型の値について出力
Print(ValueDescription(InpShortValue)); // ushort型の値について出力
/*
出力結果の例:
ulong value: 1
ulong value: 72057594037927936 using MathSwap()
binary ulong value: 0000000000000000000000000000000000000000000000000000000000000001
binary ulong value: 0000000100000000000000000000000000000000000000000000000000000000 using MathSwap()
uint value: 2
uint value: 33554432 using MathSwap()
binary uint value: 00000000000000000000000000000010
binary uint value: 00000010000000000000000000000000 using MathSwap()
ushort value: 3
ushort value: 768 using MathSwap()
binary ushort value: 0000000000000011
binary ushort value: 0000001100000000 using MathSwap()
*/
}
//+------------------------------------------------------------------+
//| 変数の数値とその説明を含むテキストを返す関数 |
//+------------------------------------------------------------------+
template <typename T>
string ValueDescription(T x)
{
// num_bitsに変数のビット数を格納
int num_bits = sizeof(T)*8;
// 変数の型名を取得
string type_name = typename(T);
// NumberToBinaryString関数を使い、xの2進数表現を文字列に変換
string bin_x = NumberToBinaryString(x);
// MathSwap関数でバイト順を反転させた値を2進数表現に変換
string bin_swap_x = NumberToBinaryString(MathSwap(x));
// 変換前と変換後の10進数および2進数表現を整形して文字列で返す
return(StringFormat(
"%s value: %lld\n%s value: %lld using MathSwap()\n"
"binary %s value: %0*s\nbinary %s value: %0*s using MathSwap()\n",
type_name, x, // 変数の型名と元の数値(10進数)
type_name, MathSwap(x), // 変数の型名とバイト順を反転した数値(10進数)
type_name, num_bits, bin_x, // 変数の型名、ビット数、元の数値の2進数表現
type_name, num_bits, bin_swap_x // 変数の型名、ビット数、反転後の数値の2進数表現
));
}
//+------------------------------------------------------------------+
//| 数値を2進数表現の文字列として返す関数 |
//+------------------------------------------------------------------+
template <typename T>
string NumberToBinaryString(T x)
{
// 2進数表現を格納する文字列
string res = "";
// 最初のビット位置を初期化
int i = -1;
// サイズ(ビット数)をuchar型で保持
uchar size = sizeof(T)*8-1;
// マスクを用いて指定されたビットの位置をチェック
ulong mask = (ulong)1<<size;
// 左シフトしながら最初のビットが「1」となる位置まで進める
while(!((x<<++i) & mask));
// ビットごとに「0」か「1」を判定し、結果の文字列に追加
for(; i <= size; i++)
res += !((x<<i) & mask) ? "0" : "1";
// 生成した2進数表現の文字列を返す
return res;
}
このコードは、指定された数値(ulong型、uint型、ushort型の各型)をMathSwap関数でバイト順を反転し、元の値と反転後の値を10進数と2進数の両方でエキスパートログに出力します。
まず、各型の入力値が与えられ、それぞれの値に対してバイト順が反転されます。次に、反転前と反転後の10進数値、および対応する2進数表現が文字列として作成され、エキスパートログに整形出力されます。このため、バイト順が異なる数値がどのように見えるかを視覚的に確認することができます。
サンプルコード解説1:グローバル領域部分
#property script_show_inputs
// ulong型の入力値、任意の数値を設定できる
input ulong InpLongValue = 1;
// uint型の入力値、任意の数値を設定できる
input uint InpIntValue = 2;
// ushort型の入力値、任意の数値を設定できる
input ushort InpShortValue = 3;
このコードのグローバル領域には、スクリプトの実行設定および入力パラメータが定義されています。
#property script_show_inputs
「#property script_show_inputs」は、スクリプトに設定されたプロパティで、スクリプト実行時に入力パラメータの設定画面を表示する指定です。これにより、スクリプトが起動されると、入力パラメータを設定できる画面が表示されます。
入力パラメータ
このスクリプトでは、以下の3つの入力パラメータが定義されています。
- InpLongValueは、ulong型の入力パラメータで、任意の値を設定できるようになっています。データ型は64ビットの符号なし整数で、ulong型の数値がバイト順を反転する処理に使用されます。
- InpIntValueは、uint型の入力パラメータで、任意の値を設定できます。このパラメータは32ビットの符号なし整数型で、uint型の数値をバイト順に反転する処理に使用されます。
- InpShortValueは、ushort型の入力パラメータで、同じく任意の値を設定でき、16ビットの符号なし整数型です。この数値もバイト順を反転する処理に利用されます。
これらの入力パラメータはスクリプト起動時に設定が可能で、指定した数値がそのままバイト順の反転処理に適用されます。
サンプルコード解説2:OnStart関数部分
/+------------------------------------------------------------------+
//| スクリプトプログラムの開始関数 |
//+------------------------------------------------------------------+
void OnStart()
{
// 入力値をMathSwap関数でバイト順を反転し、
// 元の数値と変換後の数値を10進数および2進数表現でエキスパートログに出力する
Print(ValueDescription(InpLongValue)); // ulong型の値について出力
Print(ValueDescription(InpIntValue)); // uint型の値について出力
Print(ValueDescription(InpShortValue)); // ushort型の値について出力
/*
出力結果の例:
ulong value: 1
ulong value: 72057594037927936 using MathSwap()
binary ulong value: 0000000000000000000000000000000000000000000000000000000000000001
binary ulong value: 0000000100000000000000000000000000000000000000000000000000000000 using MathSwap()
uint value: 2
uint value: 33554432 using MathSwap()
binary uint value: 00000000000000000000000000000010
binary uint value: 00000010000000000000000000000000 using MathSwap()
ushort value: 3
ushort value: 768 using MathSwap()
binary ushort value: 0000000000000011
binary ushort value: 0000001100000000 using MathSwap()
*/
}
OnStart関数は、スクリプトが実行されたときに最初に呼び出される関数です。この関数では、スクリプトが受け取った各入力値(InpLongValue、InpIntValue、InpShortValue)について、MathSwap関数を用いてバイト順を反転し、その結果をエキスパートログに出力します。
関数の処理内容
このOnStart関数では、以下の処理が行われます。
- InpLongValue(ulong型)、InpIntValue(uint型)、InpShortValue(ushort型)のそれぞれについて、ValueDescription関数が呼ばれます。
- ValueDescription関数は、MathSwap関数を使用して各値のバイト順を反転し、元の値と反転後の値を10進数および2進数の形式で出力します。
- たとえば、InpLongValueが「1」の場合、バイト順が反転されると「72057594037927936」という値になり、その結果がエキスパートログに表示されます。
このようにして、各入力値の元の数値とバイト順を反転した数値が、両方ともエキスパートログにわかりやすく表示されます。
サンプルコード解説3:ValueDescription関数部分
//+------------------------------------------------------------------+
//| 変数の数値とその説明を含むテキストを返す関数 |
//+------------------------------------------------------------------+
template <typename T>
string ValueDescription(T x)
{
// num_bitsに変数のビット数を格納
int num_bits = sizeof(T)*8;
// 変数の型名を取得
string type_name = typename(T);
// NumberToBinaryString関数を使い、xの2進数表現を文字列に変換
string bin_x = NumberToBinaryString(x);
// MathSwap関数でバイト順を反転させた値を2進数表現に変換
string bin_swap_x = NumberToBinaryString(MathSwap(x));
// 変換前と変換後の10進数および2進数表現を整形して文字列で返す
return(StringFormat(
"%s value: %lld\n%s value: %lld using MathSwap()\n"
"binary %s value: %0*s\nbinary %s value: %0*s using MathSwap()\n",
type_name, x, // 変数の型名と元の数値(10進数)
type_name, MathSwap(x), // 変数の型名とバイト順を反転した数値(10進数)
type_name, num_bits, bin_x, // 変数の型名、ビット数、元の数値の2進数表現
type_name, num_bits, bin_swap_x // 変数の型名、ビット数、反転後の数値の2進数表現
));
}
この関数は、指定された変数の数値について、元のバイト順の数値と、MathSwap関数を用いてバイト順を反転した数値の両方を10進数と2進数で表示するための文字列を返します。
templateとtypenameの解説
templateとは
templateは「型を指定せずに関数やクラスを作るための仕組み」です。通常、関数には型(例えば、int型やfloat型など)を指定して作成しますが、templateを使うと、どの型でも使える関数を作ることができます。このため、ValueDescription関数はint型、float型などに限らず、ulong型、uint型、ushort型といった異なる型でも同じ処理を行えるようになります。
templateを使うときは、関数の前に「template 」と記述します。Tは、この関数が使われるときに指定される型のことです。たとえば、この関数にuint型を渡すとTはuint型となり、関数内でTはuint型として扱われます。
typenameとは
typenameは、templateの中で「型を表すもの」として使います。templateで型の指定が決まると、その型がtypenameTという形で指定され、関数内でTは実際に渡された型(例えば、uint型やulong型など)になります。したがって、typenameは「型を扱うための記号」だと理解するとよいでしょう。
sizeofの解説
sizeof演算子は「指定した型や変数が何バイトか」を調べるための演算子です。たとえば、「sizeof(int)」と記述するとint型が何バイトかを取得できます。ここでは「sizeof(T)」とすることで、T型が何バイトかを取得し、その値に8を掛けることで「その型のビット数(8ビット×バイト数)」を計算しています。このビット数はnum_bitsという変数に格納されます。
関数の処理内容
- ビット数の取得
num_bitsには、指定された型のビット数が格納されます。T型のバイト数を調べ、8を掛けることでビット数を計算します。 - 型名の取得
type_nameには、変数の型名が格納されます。これは出力に用いるためです。 - 2進数表現への変換
NumberToBinaryString関数を使って、x(入力された値)を2進数表現の文字列に変換します。これがbin_xに格納されます。 - MathSwap関数でのバイト順反転
MathSwap関数でxのバイト順を反転させ、反転後の値を2進数に変換した結果をbin_swap_xに格納します。 - 文字列の整形
StringFormat関数を使用して、元の値と反転後の値を10進数と2進数でわかりやすく整形し、最終的な出力内容を文字列として返します。
StringFormat関数のフォーマット指定子と対応する値
StringFormat関数は、指定した形式に合わせて文字列を整形するために使います。フォーマット指定子とは、出力する内容をどのように表示するかを決める記号のようなもので、以下のようなフォーマット指定子が使われています。
フォーマット指定子と対応する値
- %s
「文字列」を表すフォーマット指定子です。ここには変数の型名(type_name)が入ります。例えば、type_nameが「uint」の場合、%sには「uint」という文字列が入ります。 - %lld
「10進数の整数(long型 int型)」を表すフォーマット指定子です。このコードでは、xとMathSwap(x)の数値が%lldで表されます。xが1のとき、ここには「1」が入り、MathSwap(x)が「72057594037927936」ならその値が入ります。 - %0s
「0埋めした指定の桁数の文字列」を表します。例えば、%064sは64桁の2進数として出力し、桁数が不足する場合は「0」を追加して埋めます。このコードでは、元の数値やMathSwap関数後の数値の2進数表現(bin_xやbin_swap_x)が入ります。
サンプルコード解説4:NumberToBinaryString関数部分
//+------------------------------------------------------------------+
//| 数値を2進数表現の文字列として返す関数 |
//+------------------------------------------------------------------+
template <typename T>
string NumberToBinaryString(T x)
{
// 2進数表現を格納する文字列
string res = "";
// 最初のビット位置を初期化
int i = -1;
// サイズ(ビット数)をuchar型で保持
uchar size = sizeof(T)*8-1;
// マスクを用いて指定されたビットの位置をチェック
ulong mask = (ulong)1<<size;
// 左シフトしながら最初のビットが「1」となる位置まで進める
while(!((x<<++i) & mask));
// ビットごとに「0」か「1」を判定し、結果の文字列に追加
for(; i <= size; i++)
res += !((x<<i) & mask) ? "0" : "1";
// 生成した2進数表現の文字列を返す
return res;
}
この関数は、指定された数値を2進数の文字列として表現し、その結果を返します。具体的には、ビットごとに「0」や「1」を判定して文字列として組み立て、入力値を2進数で見られるようにする仕組みです。
関数の処理内容
res変数の定義
resという空の文字列を用意します。ここに、入力された数値の2進数表現(「0」や「1」の文字列)を順に追加していきます。
ビット位置の初期化
変数iを-1で初期化します。この変数iはビット位置を示し、後にどのビットを確認しているかを表します。初期値を-1にしている理由は、次のwhileループでインクリメント(++i)することで0番目のビットから確実に確認を始められるようにするためです。
ビットサイズの計算とマスクの準備
変数sizeには、入力された数値のビット数から1を引いた値を格納します。例えば、32ビットの数値ならsizeは31、64ビットなら63です。
次に、マスクを作成します。この部分のコード「ulong mask = (ulong)1 << size;」では、maskという変数に特定のビットだけをチェックするための基準(マスク)を設定しています。文法構造を順に説明します。
(ulong)1
は、数値「1」をulong型に変換しています。ulong型は64ビットの符号なし整数型で、大きな数値を扱える型です。ここでは「1」をulong型に変換することで、次に行うシフト操作を確実に行えるようにしています。<< size
は、左シフト演算子を使って数値を左にビットシフト(ビットをずらす)しています。ここでは(ulong)1をsizeの回数だけ左にシフトします。たとえば、sizeが31であれば、1を31回左にシフトするので、maskには32ビット目が「1」で残りが「0」の値が格納されます。- maskは、特定のビット位置をチェックする基準として機能します。これにより、指定したビットが1か0かを確認することができるようになります。
最初の「1」のビット位置を探す
whileループでは、x
を1ビットずつ左にシフトしながら、最初に「1」が現れるビット位置を見つけます。この部分のコード「while(!((x << ++i) & mask));」の構造について詳しく説明します。
x << ++i
:これは、変数x
を左にシフトする操作です。<<
は「左シフト演算子」と呼ばれ、数値のビットを左にずらすために使います。++i
はi
の値を1増やしてから使う、という意味です。最初のループでは、iが0から1に増え、次のビットに移ります。x << ++i
は、x
のビット列をi
ビット左にシフトし、順に各ビットを確認していくための操作です。こうして左シフトすることで、ビット列の最も左のビットを順番に確認できるようにしています。(x << ++i) & mask
:&
は「ビット単位論理積」と呼ばれる演算子で、2つのビットを比較して両方が1の場合にのみ1を返す操作です。mask
は特定のビット位置を判定するために使われます。この場合、x << ++i
とmask
のビットごとに論理積を取り、左シフト後のビットが「1」か「0」かを判定します。シフトしたx
のビットとmask
のビットを比較し、「1」の位置が見つかるまでループを続けます。- while(!(…)):
!
は「否定演算子」で、中の式が「false」のとき「true」にする役割です。ここでは、「(x << ++i) & mask」が0(つまり「1」が見つかっていない状態)の場合にループが続き、最初に「1」が見つかるとループが終了します。
このループは、x
を1ビットずつ左にシフトしながら、ビット列の中で最初に「1」が現れる位置を見つける操作を行っています。これにより、ビット列の中で「1」が初めて出現する位置から2進数をチェックしていけるようにしています。
ビットを確認し、結果を文字列に追加する
次に来るforループでは、iから最下位ビットまでの各ビットを確認し、ビットが「0」であれば「0」を、ビットが「1」であれば「1」をresに追加していきます。この部分のコード「for(; i <= size; i++) res += !((x << i) & mask) ? “0” : “1”;」を詳しく解説します。
- for(; i <= size; i++):これはforループの記述です。
- 開始条件として、変数iは最初にwhileループ内で設定された位置からスタートします(たとえば、最初に「1」が出現したビット位置)。
- 終了条件として、iがsize以下の間、このループが繰り返されます。sizeはビットの数から1を引いたもので、ループがビットの末尾まで進むように指定しています。
- i++はループごとにiを1つ増やす意味です。このため、ループが1ビットずつ右へ進みながら次のビットを確認していきます。
(x << i) & mask
:この部分では、x
をi回左にシフトしてビットを確認しています。x << i
は、変数xのビット列をi回左にシフトして、現在注目しているビットをマスクの位置まで持っていきます。たとえば、iが2の場合、x
のビットが2ビット左に移動し、3ビット目が「1」か「0」かを確認できるようにします。- & maskは、「ビット単位論理積」を意味します。maskと
x << i
のビットごとに論理積を取ることで、確認中のビットが「1」か「0」かを調べます。もしそのビットが「1」なら結果は「1」になり、そうでなければ「0」になります。 !((x << i) & mask)
:!
は否定演算子で、この式が「0」(つまりビットが「0」)ならtrue
になります。「1」ならfalse
です。? "0" : "1"
:?
と:
を使ったこの部分は「三項演算子」と呼ばれるもので、簡単な条件分岐を行います。!((x << i) & mask)
がtrue
なら(つまりビットが「0」なら)"0"
を選びます。false
なら(ビットが「1」なら)"1"
を選びます。res += ...
:この部分で、判定した「0」または「1」の結果を文字列resに追加します。たとえば、現在のビットが「1」であれば"1"
が追加され、次のビットが「0」であれば"0"
が追加されます。ループが進むごとにres
にビットの内容が順に追加されていき、最終的に入力値の2進数表現の文字列が完成します。
完成した2進数文字列を返す
すべてのビットが確認されると、resには入力された数値の2進数表現が完成しています。この文字列を返すことで、呼び出し元で入力値の2進数としての表現を利用できるようになります。