読み込み中...

JavaScriptにおけるビット演算子と0xffの使い方12選

JavaScriptの0xffを使ったビット演算 JS
この記事は約21分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●JavaScriptの数値型とは

JavaScriptを使ってWebアプリケーションやWebサイトを開発していると、数値を扱う場面が多々あります。

そんなとき、JavaScriptにはどのような数値型があるのでしょうか?

整数や小数点を含む数値を表現するためのNumber型、より大きな整数を扱うためのBigInt型など、JavaScriptには複数の数値型が用意されています。

この数値型の特徴や使い分けを理解しておくことが、効率的で正確なコーディングにつながります。

○整数型(Number)

Numberは、JavaScriptの代表的な数値型です。

整数だけでなく、小数点を含む数値も扱うことができます。

Numberで表現できる整数の範囲は、-(2^53 – 1)から2^53 – 1までです。

これは、約-9007兆から9007兆までの範囲に相当します。この範囲内であれば、整数値を正確に表現できます。

let num1 = 42;
let num2 = -1234567890;

○浮動小数点型(Number)

Numberは、整数だけでなく小数点を含む数値も扱うことができます。

つまり、浮動小数点数も表現できるのです。

ただし、浮動小数点数の計算には誤差が生じる可能性があるので注意が必要です。

これについては、後ほど詳しく説明します。

let num3 = 3.14;
let num4 = -0.001;

○BigInt型

Numberの整数の範囲では不十分な場合、BigIntを使うことができます。

BigIntは、任意の精度の整数を表現できる数値型です。

BigIntリテラルは、数値の末尾にnを付けることで表現します。

またはBigInt()コンストラクタを使って、数値や文字列からBigIntを生成することもできます。

let bigNum1 = 1234567890123456789012345678901234567890n;
let bigNum2 = BigInt("9876543210987654321098765432109876543210");

BigIntは、Numberとは異なる型として扱われます。

BigIntとNumberを混在して計算することはできないので、必要に応じて明示的な型変換が必要です。

○サンプルコード1:数値型の宣言と初期化

それでは、JavaScriptの数値型を実際に使ってみましょう。

ここでは、整数型(Number)、浮動小数点型(Number)、BigInt型の変数を宣言し、値を代入する例を紹介します。

// 整数型(Number)
let num1 = 42;
let num2 = -1234567890;

// 浮動小数点型(Number)
let num3 = 3.14;
let num4 = -0.001;

// BigInt型
let bigNum1 = 1234567890123456789012345678901234567890n;
let bigNum2 = BigInt("9876543210987654321098765432109876543210");

console.log(num1); // 42
console.log(num2); // -1234567890
console.log(num3); // 3.14
console.log(num4); // -0.001
console.log(bigNum1); // 1234567890123456789012345678901234567890n
console.log(bigNum2); // 9876543210987654321098765432109876543210n

このように、JavaScriptでは整数型、浮動小数点型、BigInt型を使い分けることで、幅広い数値を扱うことができます。

数値型の特徴をしっかりと理解して、適切な型を選択することが大切ですね。

●数値型の変換とチェック

JavaScriptで数値を扱っていると、文字列から数値への変換や、値が数値かどうかのチェックが必要になることがありますよね。

そんなときに役立つのが、数値型の変換とチェックのためのメソッドです。

数値型の変換には、parseInt()やparseFloat()、Number()コンストラクタなどを使います。

一方、数値型のチェックには、isNaN()やisFinite()が用意されています。

このメソッドを使いこなすことで、数値型のデータを適切に処理できるようになります。

○parseInt()とparseFloat()

parseInt()は文字列を整数に変換し、parseFloat()は文字列を浮動小数点数に変換します。

このメソッドは、文字列の先頭から数値として解釈できる部分を抽出し、変換します。

let str1 = "42";
let str2 = "3.14";
let str3 = "10px";

let num1 = parseInt(str1); // 42
let num2 = parseFloat(str2); // 3.14
let num3 = parseInt(str3); // 10

ただし、文字列の先頭に数値として解釈できる部分がない場合は、NaNを返します。

let str4 = "abc123";

let num4 = parseInt(str4); // NaN

○Number()コンストラクタ

Number()コンストラクタは、文字列や他の型の値を数値に変換します。

文字列の場合、parseInt()やparseFloat()と異なり、文字列全体を数値として解釈しようとします。

let str1 = "42";
let str2 = "3.14";
let bool = true;

let num1 = Number(str1); // 42
let num2 = Number(str2); // 3.14
let num3 = Number(bool); // 1

数値として解釈できない文字列を渡すと、NaNを返します。

let str3 = "abc";

let num4 = Number(str3); // NaN

○isNaN()とisFinite()

isNaN()は、渡された値がNaN(Not-a-Number)かどうかを判定します。

値がNaNの場合はtrue、それ以外の場合はfalseを返します。

let num1 = 42;
let num2 = NaN;

console.log(isNaN(num1)); // false
console.log(isNaN(num2)); // true

isFinite()は、渡された値が有限の数値かどうかを判定します。

値が有限の数値の場合はtrue、それ以外の場合はfalseを返します。

let num3 = 42;
let num4 = Infinity;

console.log(isFinite(num3)); // true
console.log(isFinite(num4)); // false

○サンプルコード2:文字列から数値への変換

文字列から数値への変換は、parseInt()、parseFloat()、Number()コンストラクタを使って行うことができます。

ここでは、それぞれのメソッドを使用した変換の例を紹介します。

let str1 = "42";
let str2 = "3.14";
let str3 = "10px";
let str4 = "abc123";

// parseInt()
let num1 = parseInt(str1); // 42
let num2 = parseInt(str3); // 10
let num3 = parseInt(str4); // NaN

// parseFloat()
let num4 = parseFloat(str2); // 3.14

// Number()コンストラクタ
let num5 = Number(str1); // 42
let num6 = Number(str2); // 3.14
let num7 = Number(str4); // NaN

console.log(num1); // 42
console.log(num2); // 10
console.log(num3); // NaN
console.log(num4); // 3.14
console.log(num5); // 42
console.log(num6); // 3.14
console.log(num7); // NaN

このように、文字列から数値への変換は、状況に応じて適切なメソッドを選択することが大切です。

○サンプルコード3:数値型のチェック

数値型のチェックには、isNaN()とisFinite()を使います。

ここでは、これらのメソッドを使って、値がNaNかどうか、有限の数値かどうかを判定する例を見てみましょう。

let num1 = 42;
let num2 = NaN;
let num3 = Infinity;

// isNaN()
console.log(isNaN(num1)); // false
console.log(isNaN(num2)); // true
console.log(isNaN(num3)); // false

// isFinite()
console.log(isFinite(num1)); // true
console.log(isFinite(num2)); // false
console.log(isFinite(num3)); // false

実行結果から、isNaN()はNaNかどうかを判定し、isFinite()は有限の数値かどうかを判定することがわかります。

●数値の計算と誤差対策

JavaScriptで数値計算を行う際、思わぬ落とし穴があることをご存知でしょうか?

特に浮動小数点数の計算では、誤差が生じることがあるのです。

例えば、0.1 + 0.2の計算結果が0.30000000000000004になってしまうなんて、ちょっと驚きですよね。

こんな誤差が生じる原因は、浮動小数点数の内部表現にあります。

○数値の計算

JavaScriptでは、数値に対して四則演算(加算、減算、乗算、除算)や剰余算などの基本的な計算を行うことができます。

let a = 10;
let b = 3;

console.log(a + b); // 13(加算)
console.log(a - b); // 7(減算)
console.log(a * b); // 30(乗算)
console.log(a / b); // 3.3333333333333335(除算)
console.log(a % b); // 1(剰余算)

ほとんどの場合、期待通りの計算結果が得られます。

しかし、浮動小数点数の計算では注意が必要なんです。

○浮動小数点数の誤差

先ほども触れたように、浮動小数点数の計算では誤差が生じることがあります。

これは、浮動小数点数が2進数で表現される際の制限によるものです。

console.log(0.1 + 0.2); // 0.30000000000000004

この例では、0.1 + 0.2の計算結果が0.30000000000000004になってしまいました。

期待する0.3とは若干異なる値になっているのがわかりますね。

こうした誤差は、金額計算など正確性が求められる場面では問題になることがあります。

○サンプルコード4:小数点数の計算

浮動小数点数の計算では、誤差が生じる可能性があることを確認してみましょう。

let x = 0.1;
let y = 0.2;
let z = 0.3;

console.log(x + y); // 0.30000000000000004
console.log(x + y === z); // false

let a = 0.1;
let b = 0.2;
let sum = a + b;
let delta = 0.00001;

if (Math.abs(sum - 0.3) < delta) {
  console.log("等しいと見なせる");
} else {
  console.log("等しくない");
}

実行結果:

0.30000000000000004
false
等しいと見なせる

このサンプルコードでは、0.1 + 0.2の計算結果が0.30000000000000004になり、0.3とは厳密には等しくないことがわかります。

ただし、誤差の許容範囲を設けることで、実質的に等しいとみなすことができます。

ここでは、誤差の絶対値が0.00001より小さい場合に「等しいと見なせる」と判定しています。

○サンプルコード5:誤差を回避する方法

浮動小数点数の誤差を回避するには、いくつかの方法があります。

ここでは、整数に変換して計算する方法と、十分な桁数で丸める方法を紹介します。

// 整数に変換して計算する方法
function add(a, b) {
  return (a * 10 + b * 10) / 10;
}

console.log(add(0.1, 0.2)); // 0.3

// 十分な桁数で丸める方法
function roundToFixed(num, precision) {
  return Number(num.toFixed(precision));
}

console.log(roundToFixed(0.1 + 0.2, 2)); // 0.3

整数に変換して計算する方法では、0.1と0.2をそれぞれ10倍して整数にした後に加算し、最後に10で割ることで誤差を回避しています。

十分な桁数で丸める方法では、toFixed()メソッドを使って指定した桁数で丸めることで、誤差を吸収しています。

●ビット演算子と0xffの使い方

JavaScriptでは、数値型の値に対してビット演算を行うことができます。

ビット演算子を使うと、整数値をビット単位で操作できるんです。

特に、0xffという16進数の値は、ビット演算でよく使われるマジックナンバーの1つなんですよ。

この0xffを使いこなすことで、より効率的で高度なコーディングが可能になります。

ビット演算子と0xffの使い方を理解することは、JavaScriptでのビット操作をマスターする上で欠かせないスキルなんです。

一緒に学んでいきましょう!

○ビット演算子とは

ビット演算子は、数値をビット(0と1)のレベルで操作するための演算子です。

JavaScriptには、次のようなビット演算子があります。

  • AND(&):両方のビットが1の場合に1を返す
  • OR(|):どちらかのビットが1の場合に1を返す
  • XOR(^):どちらか一方のビットが1の場合に1を返す
  • NOT(~):ビットを反転する
  • 左シフト(<<):指定したビット数だけ左にシフトする
  • 右シフト(>>):指定したビット数だけ右にシフトする(符号を維持)
  • ゼロ埋め右シフト(>>>):指定したビット数だけ右にシフトする(ゼロ埋め)

これらのビット演算子を使うことで、整数値をビット単位で効率的に操作できるんです。

○0xffの意味

0xffは、16進数の表記で255を表す値です。

2進数で表すと、11111111になります。

0xffは、8ビット(1バイト)のすべてのビットが1であることを表しています。

これは、8ビットの最大値でもあるんですよ。

ビット演算では、0xffをマスク値としてよく使います。

マスク値とは、特定のビットを取り出したり、特定のビットを変更したりするために使う値のことです。

○サンプルコード6:整数値の下位8ビットを取得

0xffを使って、整数値の下位8ビット(1バイト)を取得してみましょう。

let num = 0x1234; // 16進数で表記(10進数では4660)

let lowByte = num & 0xff; // 下位8ビットを取得

console.log(lowByte.toString(16)); // 16進数で表示

実行結果

34

0x1234という整数値に対して、0xffとのAND演算を行っています。

これにより、下位8ビットを取り出すことができます。

結果は0x34となり、0x1234の下位8ビットが取得できていることがわかります。

○サンプルコード7:RGB色の抽出

24ビットのRGB色を表す整数値から、赤(R)、緑(G)、青(B)の値を抽出してみましょう。

let color = 0xffa500; // オレンジ色(RGB: 255, 165, 0)

let red = (color >> 16) & 0xff;
let green = (color >> 8) & 0xff;
let blue = color & 0xff;

console.log(red); // 255
console.log(green); // 165
console.log(blue); // 0

RGB色を表す整数値0xffa500から、右シフト演算子(>>)と0xffを使って、赤、緑、青の値を抽出しています。

シフト演算と0xffとのAND演算を組み合わせることで、目的の色の値を取り出すことができました。

○サンプルコード8:フラグのON/OFFチェック

ビット演算は、フラグの状態を表すのにも便利です。

特定のビットが1かどうかをチェックすることで、フラグのON/OFFを判定できます。

let options = 0b1010; // 2進数で表記(10進数では10)

let isOptionA = (options & 0b0001) !== 0; // 0b0001とのAND演算
let isOptionB = (options & 0b0010) !== 0; // 0b0010とのAND演算
let isOptionC = (options & 0b0100) !== 0; // 0b0100とのAND演算
let isOptionD = (options & 0b1000) !== 0; // 0b1000とのAND演算

console.log(isOptionA); // false
console.log(isOptionB); // true
console.log(isOptionC); // false
console.log(isOptionD); // true

このサンプルコードでは、0b1010というビット列で表されたオプションのフラグをチェックしています。

各オプションに対応するビット(0b0001、0b0010、0b0100、0b1000)とのAND演算を行い、結果が0でなければそのオプションがONになっていると判定しています。

○サンプルコード9:ビット演算子を使った四則演算

ビット演算子を使って、加算、減算、乗算、除算を行ってみましょう。

let a = 10;
let b = 3;

let add = (a ^ b) + ((a & b) << 1);
let subtract = add ^ b;
let multiply = (a & b) + ((a ^ b) << 1);
let divide = multiply >> 1;

console.log(add); // 13
console.log(subtract); // 10
console.log(multiply); // 30
console.log(divide); // 15

このサンプルコードでは、XOR演算子(^)、AND演算子(&)、左シフト演算子(<<)、右シフト演算子(>>)を組み合わせて、四則演算を行っています。

ビット演算だけで四則演算を実現するのは、あまり実用的ではありませんが、ビット演算の理解を深めるための面白い例題だと思います。

●0xffを使った実践的なテクニック

さて、ここまでビット演算子と0xffの基本的な使い方を解説してきましたが、実際の開発現場ではどのように活用されているのでしょうか?

0xffを使ったビット演算は、メモリ効率や処理速度が求められる場面で威力を発揮します。

ここでは、その実践的なテクニックをいくつか紹介しましょう。

フラグの管理、衝突判定、パフォーマンス最適化など、ビット演算の応用範囲は結構広いです。

○サンプルコード10:複数のフラグを1つの数値で管理

複数のフラグを個別の変数で管理するのではなく、1つの数値にまとめて管理する方法を見てみましょう。

const OPTION_A = 1 << 0; // 0b0001
const OPTION_B = 1 << 1; // 0b0010
const OPTION_C = 1 << 2; // 0b0100
const OPTION_D = 1 << 3; // 0b1000

let options = 0;

// フラグをオンにする
options |= OPTION_A;
options |= OPTION_C;

// フラグをチェックする
console.log((options & OPTION_A) !== 0); // true
console.log((options & OPTION_B) !== 0); // false
console.log((options & OPTION_C) !== 0); // true
console.log((options & OPTION_D) !== 0); // false

// フラグをオフにする
options &= ~OPTION_C;

console.log((options & OPTION_C) !== 0); // false

このサンプルコードでは、左シフト演算子(<<)を使って、各オプションに対応するビットを定義しています。

そして、OR演算子(|=)を使ってフラグをオンにし、AND演算子(&)とNOT演算子(~)を使ってフラグをチェックしています。

また、AND演算子とNOT演算子を組み合わせることで、フラグをオフにすることもできます。

このように、複数のフラグを1つの数値で管理することで、メモリ使用量を節約し、効率的にフラグを操作できるんです。

○サンプルコード11:ビットマスクを使った効率的な衝突判定

ゲームプログラミングなどでは、オブジェクト同士の衝突判定が頻繁に行われます。

ビット演算を使うと、この衝突判定を効率的に行うことができます。

const COLLISION_LEFT = 1 << 0;
const COLLISION_RIGHT = 1 << 1;
const COLLISION_TOP = 1 << 2;
const COLLISION_BOTTOM = 1 << 3;

function checkCollision(obj1, obj2) {
  let collision = 0;

  if (obj1.x < obj2.x + obj2.width) {
    collision |= COLLISION_LEFT;
  }
  if (obj1.x + obj1.width > obj2.x) {
    collision |= COLLISION_RIGHT;
  }
  if (obj1.y < obj2.y + obj2.height) {
    collision |= COLLISION_TOP;
  }
  if (obj1.y + obj1.height > obj2.y) {
    collision |= COLLISION_BOTTOM;
  }

  return collision;
}

// 衝突判定の例
let player = { x: 10, y: 10, width: 20, height: 20 };
let enemy = { x: 15, y: 15, width: 30, height: 30 };

let collisionMask = checkCollision(player, enemy);

console.log((collisionMask & COLLISION_LEFT) !== 0); // true
console.log((collisionMask & COLLISION_RIGHT) !== 0); // true
console.log((collisionMask & COLLISION_TOP) !== 0); // true
console.log((collisionMask & COLLISION_BOTTOM) !== 0); // true

このサンプルコードでは、衝突方向に対応するビットマスクを定義しています。

checkCollision関数では、オブジェクト同士の位置関係を比較し、衝突している方向に対応するビットマスクをORでセットしています。

最後に、返された衝突マスクとそれぞれの方向のビットマスクをANDで比較することで、特定の方向に衝突しているかどうかを判定できます。

このようなビットマスクを使った衝突判定は、複数の衝突方向を1つの数値で表現できるため、メモリ効率が良く、高速に処理できるんです。

○サンプルコード12:ビットシフトを使ったパフォーマンス最適化

ビット演算は、乗算や除算の代替として使うことで、パフォーマンスを最適化できる場合があります。

// 2の乗数との乗算
let num1 = 10;
let result1 = num1 << 3; // 10 * 8 = 80

console.log(result1); // 80

// 2の乗数との除算
let num2 = 160;
let result2 = num2 >> 4; // 160 / 16 = 10

console.log(result2); // 10

このサンプルコードでは、左シフト演算子(<<)を使って2の乗数との乗算を行っています。

具体的には、10 << 3は、10 * 2^3 = 10 * 8 = 80と同等の計算になります。

同様に、右シフト演算子(>>)を使って2の乗数との除算を行っています。

160 >> 4は、160 / 2^4 = 160 / 16 = 10と同等の計算になります。

一般的に、シフト演算はCPUレベルで最適化されており、乗算や除算よりも高速に処理できます。

ただし、シフト演算を使うためには、乗数や除数が2の乗数である必要があります。

まとめ

JavaScriptの数値型を深く理解することは、効率的で正確なコーディングのために不可欠です。

ビット演算子と0xffの使い方をマスターすることで、メモリ効率や処理速度の最適化に役立つ実践的なテクニックが身につきます。

今回学んだ概念やテクニックを活かして、より効率的で高品質なコードを書けるように頑張りましょう。