ビット演算で桁数を合わせるために256を足しているのはなぜ?
解決済
回答 5
投稿
- 評価
- クリップ 0
- VIEW 2,952
下記リンク先で、桁を合わせるため、(r << 8)を(256 + r << 8)へ変更しているのですが、
・256を足すと、どうして2進数『1000000000000000000000000』を足したことになるのでしょうか?
2進数で表すところの『1000000000000000000000000』、10進数で『16777216』、16進数で『1000000』を足すことによって一時的に桁を合わせ
function toColorCode_1a(r,g,b){
return (((256 + r << 8) + g << 8) + b).toString(16).replace(/^1/,'#');
}
・1バイトが256だから?
・11111111?
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
>・256を足すと、どうして2進数『1000000000000000000000000』を足したことになるのでしょうか?
256を足したから1000000000000000000000000を足したことになっているのではなく
この256はr<<8とg<<8の8bit左シフト2回によって0x100 が 0x1000000 になっています。
256 + r << 8 の部分で256(0x100)は0x10000になっていて(1回目の8bit左シフト)
次の「(その演算結果+g)<<8」によって最初の256だった物はさらに8bit左にシフトされるため
結果として最初の256(16進の0x100)は最終的に0x1000000になっているという事です。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
演算子の優先順位を考慮してビットシフトよりも先に足し算が行われるので
(((256 + r << 8) + g << 8) + b)
の計算の順番は16進数で考えて以下の通りになると思います。(例えば、r=1,b=2,g=0x80とすると)
256+r==>0x100+0x01=0x101
(256+r)<<8==>0x101<<8=0x10100
((256+r)<<8)+g==>0x10100+0x02=0x10102
((256+r)<<8)+g<<8==>0x10102<<8=0x1010200
(((256+r)<<8)+g<<8)+b==>0x1010200+0x80=0x1010280
最上位ビットの'1'を#に置換して
"#010280"
になります。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
桁落ちの問題
単純に考えれば赤を16ビットシフト、緑を8ビットシフトすればいいですが、それでは赤が 0
の場合に桁落ちが発生してしまいます。
'use strict';
function toColorCode (r, g, b) {
return '#' + ((r << 16) + (g << 8) + b).toString(16);
}
console.log(toColorCode(255, 255, 255)); // "ffffff"
console.log(toColorCode(0, 0, 128)); // "#80"
桁落ちの回避策
桁落ちを回避するために 0x000080
を 0x1000080
のように変換している(一つ上の桁に 1
を置く)のでしょう。
256 を加算すると9bit目に1を置く事になります。
10進数で例えるなら「9 + 100 = 109」で2桁目をゼロパディングしているようなものですね。
var r = 15;
console.log(r.toString(16)); // "f"
console.log((256 + r).toString(16)); // "10f"
console.log((0x100 + r).toString(16)); // "10f"
N進数
Nを基数とするN進数には各桁となる数字に明確な計算法があります。
10進数で例えるなら、148
は次のように分解する事が出来ます。
console.log(Math.pow(10, 0) * 8); // 8 … 1桁目は 10^0*8 (10の0乗の8倍)
console.log(Math.pow(10, 1) * 4); // 40 … 2桁目は 10^1*4 (10の1乗の4倍)
console.log(Math.pow(10, 2 * 1)); // 100 … 3桁目は 10^2*1 (10の2乗の1倍)
console.log(Math.pow(10, 2) * 1 + Math.pow(10, 1) * 4 + Math.pow(10, 0) * 8); // 148
同じ事を16進数で考えればこうなります。
console.log((Math.pow(16, 2) * 1).toString(16)); // 100 … 3桁目は 16^2*1 (16の2乗の1倍)
console.log((Math.pow(16, 2) * 1)); // 256 … 10進数なら 256
従って、256 を加算するという事は16進数でいうところの「0x100
(16進数表記)」を加算する事と同義です。
「0x100
(16進数表記)」は2進数に換算すると「0b100000000
(2進数表記)」になります。
「0b100000000
(2進数表記)」は9bit目に1がある数字です。
2進数についてはご自身で計算(検証)してみてください。
別解コード
私なら次のようにコードを書きます。
'use strict';
function toColorCode (r, g, b) {
return '#' + (0x1000000 + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
console.log(toColorCode(255, 255, 255)); // "ffffff"
console.log(toColorCode(0, 0, 128)); // "#000080"
ゼロパディングについては文字列処理をしてもいいので他の方法もあるとは思います。
更新履歴
- 2016/05/02 11:24 256加算のロジックの説明追加
- 2016/05/02 11:59 256加算のロジックの説明が誤っていたので修正
- 2016/05/03 11:55 N進数の説明追加
Re: re97 さん
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
256 は16進で 100 です。
式で8ビット左シフトを2回していますので、
1000000 になります。
これがリンク先の説明 の
「16進数で『1000000』を足すことによって」
になります。
補足
(256 + r << 8) は演算子の優先度から
256 + r の結果を 8 ビット左シフトです。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
ビット演算とか2進数とか16進数とかあまり関係ありません。ただ単に桁数を合わせたいだけです。
10進数で考えてもいいです。
9999以下の数値がいろいろあったとします。
1
12
888
2345
これを4ケタ未満だった場合に4ケタになるように0を付けたい。そういう話です。つまり
0001
0012
0888
2345
にしたいと。
そのために1万を足して
10001
10012
10888
12345
にして、先頭の1をとれば目的に文字列になるよねという話です。
初心者の方には逆にわかりにくくなっているかもしれませんが、こちらの方がシンプルですよね。
あと念のため
(((256 + r << 8) + g << 8) + b)
は
256 << 16
r << 16
g << 8
b
の4つの値を足したのと同じです。もしかするとこっちが分からなかった?
ちなみに
(256 << 16).toString(16)
は1000000です。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.35%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/05/03 10:30 編集
>256(0x100)は0x10000になっていて(1回目の8bit左シフト)
・ここが分からないのですが、これはどうやって導き出すのでしょうか?
・下記やり方で変換しているのでしょうか?
・それとも、何か簡単に導き出すことができるのでしょうか?
・256を2進数変換すると、100000000
・8bit左シフトすると、10000000000000000
・16bitへ再変換すると、10000
・だから、0x10000?
・それとも、16進数のまま8bit左シフトすることが出来るのでしょうか?
2016/05/03 12:09
>・ここが分からないのですが、これはどうやって導き出すのでしょうか?
(中略)
>・それとも、16進数のまま8bit左シフトすることが出来るのでしょうか?
16進数の値をそのまま8bit左シフトして結果を求める事はできますよ。
…うまく説明できるかわかりませんが…
256は16進数で0x100です。
これを8bit左にシフトすれば0x10000です。
「8bit左にシフトする」のは「16進数の右端に00を付加する」のと同じです。
なので0x100の右に00を付加して0x10000とすれば0x100を左に8bitシフトした結果になります。
16進数の1桁は10進数で言う所の0~15の16種類の値で1桁が構成されています。
(ただし10~15の範囲は10進のそれのままだと見た目が2桁になってしまうのでそれをA~Fに割り当てて1桁表現しています)
16種類の値で1桁が構成されそれを超えると桁があがる…から16進数です。
同様に2進数は0~1の2種類の値で1桁が構成されそれを超えると桁が上がるから2進数。
10進数は0~9の10種類の値で1桁(以下同様なので省略)。
2進数で1桁あがる(桁上りする)状況ですが、1桁が0~1の2つの値の範囲で構成されているので
1桁あがる(左にシフトする)という事は2倍されているというのと同じです。
(10進数でも1桁あがるのは10倍ですよね?)
2進数の0001は10進数の1
2進数の0010は10進数の2(上の1を2倍したのと同じ)
2進数の0100は10進数の4(上の2を2倍したのと同じ)
2進数の1000は10進数の8(上の4を2倍したのと同じ)
ちなみに多くのプログラマーはこの1,2,4,8,16,32,64,128,256,512,1024,2048,4096...と65536程度までは九九のように覚えていると思います。
この理屈を16進数でも考えてみてください。
16進数は1桁で0~Fの16種類の値で構成されていて、1桁あがるのは16倍です。
2桁あがるのは16倍してさらに16倍ですから16x16=256ですね。
「8bitシフトする」というのは「256倍する」という事と同じ事で、これはちょうど16進数で2桁の桁上りと同じです。
8bitシフトするのが何故256倍と同じなのかは上の2進数の桁上り(左シフト1回)が2倍に相当するからです。
1回の左シフトで2倍
2回なら4倍
3回なら8倍
4回なら16倍
5回なら32倍
6回なら64倍
7回なら128倍
8回なら256倍
になりますね?
なので
・256を8bit左にシフトする
↓(同じ意味)
・256を256倍する
であり
・10進数の256は16進数の0x100
・16進数で256倍するのは右に00を付加するのと同じ
・0x100を256倍した結果は0x10000
となります。
ついでなのでn進数についてもう少し。
2進数の単位(桁)をビット(bit)と呼んでいますが1桁目をbit0,2桁目をbit1,3桁目はbit2...と呼びます。
例えば 0001 という2進数は右端の1はbit0が1の状態、その左にある0はbit1が0の状態です。
2進数は 2^n という式で値を表せます。(2のn乗)
bit0 は 2^0 なので 1
bit1 は 2^1 なので 2
bit2 は 2^2 なので 4
bit3 は 2^3 なので 8
bit4 は 2^4 なので 16
10進数でも同様に 10^n で考えられます。
1桁目は 10^0 なので 1
2桁目は 10^1 なので 10
3桁目は 10^2 なので 100
4桁目は 10^3 なので 1000
5桁目は 10^4 なので 10000
16進数では
16^0 = 1 (0x0001)
16^1 = 16 (0x0010)
16^2 = 256 (0x0100)
16^3 = 4096 (0x1000)
16^4 = 8192 (0x10000)
(覚えておくと計算思考の一部を省略できるよくありがちで今回のケースに当てはまる基準)
・左シフト1回は値を2倍する事に相当する
・8bit左シフトは16進数で右端に00を付加する事に相当する
・16進数の1桁は4ビット(0~15の値範囲)に相当し2桁で0~255の256範囲に相当する
2016/05/04 11:28
>16進数の値をそのまま8bit左シフトして結果を求める事はできますよ
・初めて知りました
・「左シフト」は2進数で行うものだと思っていたのですが、根本的に勘違いしていたことに気がつきました
・大変参考になりました