- 原文地址:wiredforge.com/blog/binary…
- 原文作者:freemasen
- 译文出自:github.com/suhanyujie/…
- 本文永久链接: github.com/suhanyujie/…
- 译者:suhanyujie
正文开始
作为人类,我们通常以 10 为基数进行计数,这意味着每个状态最多可以有 10 个不同的值(0-9)。二进制数字系统则最多只能有2个不同的值, 0 或 1 。为了解决这一限制,创建了一个计数系统,其中每个位置都有一个进位,通过向对应的位添加 1 来计数。
要说明这是如何工作的,请思考下面这个计数表(tips:原文中有计数特效,可以移驾原文查看,这里只放截图):
如果单击第 2 行中的任何单元格,它将把表头中的数字添加到总值中。你也可以点击 “start counting” 按钮来观看从 0 到 255 的计数效果。
需要指出的一件有意思的细节是,每个值恰好比该值右边的所有值都大 1。
1 + 2 = 3 = 4 - 1 1 + 2 + 4 = 7 = 8 - 1 1 + 2 + 4 + 8 = 15 = 16 - 1 复制代码
2 = 1 * 2 4 = 2 * 2 8 = 4 * 2 复制代码
Bit Flags
, Bit Flags
你可以理解为上面的表中的每个数字所代表的意思。因为每个数字都表示一个位的位置,所以现在,我们可以使用这些位的数字任意组合成一个独特的值。我们使用一下这个例子。//JobState Big Flags const new_job = 1; const assigned_job = 2; const in_progress_job = 4; const complete_job = 8; const reopened_job = 16; const archived_job = 32; 复制代码
JobState
,所以这些值不再互相排斥。我们可以使用值 24 (0001 1000) 获得 complete_job
和 reopened_job
的状态,或者使用值 6 (0000 0110) 同时获得 assigned_job
和 in_progress_job
的状态。本质上,我们使用数字表示一组东西。bit shifting
。<<
和 >>
各自指向偏移的方向来表示。下面是一些位移运算的例子,注意每个例子的二进制表示都在 10 的基数之上。0000 0001 << 1 = 0000 0010 1 << 1 = 2 0000 0010 << 3 = 0001 0000 2 << 3 = 16 0001 0000 >> 1 = 0000 1000 16 >> 1 = 8 0000 1000 >> 2 = 0000 0010 8 >> 2 = 2 复制代码
JobState
比特位的例子,我们可以使用这个操作符从一个状态移动到下一个状态。job_state = new_job; job_state = job_state << 1; //job_state == assigned_job job_state = job_state << 1; //job_state == in_progress_job job_state = job_state << 1; //job_state == complete_job job_state = job_state >> 3; //job_state == new_job 复制代码
|
符号表示,它通常也作为管道字符引用。这里有几个例子,同样的,二进制数字在上面,10进制的在下面。0000 0001 | 0000 0010 = 0000 0011 1 | 2 = 3 0000 0010 | 0001 0000 = 0001 0010 2 | 16 = 18 0001 0000 | 0000 1000 = 0001 1000 16 | 8 = 24 0000 1000 | 0000 0010 = 0000 1010 8 | 2 = 10 1100 0000 | 1000 1000 = 1100 1000 192 | 136 = 200 复制代码
+
得到的结果是一样的,但是看最后一个例子。对于这两个数,最左边的 2 个位都是 1 ,但对于 | ,128 所在的位只会包含在结果中一次,而当使用 +
运算符,结果是 128 的两倍值(结果是 328)。job_state = reopened_job | assigned_job; //job_state == 18 复制代码
const new_job = 1; const assigned_job = 2; const new_assigned_job = 3; const in_progress_job = 4; const new_inprogress_job = 5 const assigned_in_progress_job = 6 const new_assigned_in_progress_job = 7 const complete_job = 8; const new_complete_job = 9; const assigned_complete_job = 10; const new_assigned_complete_job = 11; const in_progress_complete_job = 12; const new_in_progress_complete_job = 13; const assigned_in_progress_complete_job = 14; const new_assigned_in_progress_complete_job = 15; const reopened_job = 16; const new_reopened_job = 17; const assigned_reopened_job = 18; const new_assigned_reopened_job = 19; 复制代码
bitwise or
和 bitwise and
是一对好朋友,两个相似的概念有一个主要区别。与 and 的最大区别在于,当两个数都为 1 时,我们的结果应该只包含每个数为 1 的位。该操作由 &
操作符表示。这里有一些例子:1010 1010 & 0101 0101 = 0000 0000 170 & 85 = 0 1111 1111 & 0101 0101 = 0101 0101 255 & 85 = 85 1100 0000 & 1000 1000 = 1000 0000 192 & 136 = 128 复制代码
job_state = reopened_job | assigned_job; if job_state & assigned_job > 0 { //put this job in the assignee's queue //将工作放在分配的队列中 } 复制代码
job_state | new_job == 0
与另外一种选项是否得到同样的结果。如果我们需要一个完全反转的一个标志,它也许是有用的,我们的 job 状态示例可能不是最佳的实际场景,但请考虑一下以下情况。const step1 = 1; const step2 = 2; const done = 4; //start the activity let mut activity = step1; //1 //move to the next step but keep the step1 bit for audit purposes //移动到下一个步骤,但是保留步骤1(step1)以便审计或检查 activity = activity | step2; //3 //complete activity and clear audit trail //完成活动,以及清晰的审查跟踪 activity = ~activity; //4 复制代码
^
运算符表示。这个操作符将取两个数字,运算后生成一个新的数字,其中 1 的位只能在其中一边,或者另一边,而不是两者都是 1 。1010 1010 ^ 0101 0101 = 1111 1111 170 ^ 85 = 255 1111 1111 ^ 0101 0101 = 1010 1010 255 ^ 85 = 170 1100 0000 ^ 1000 1000 = 0100 1000 192 ^ 136 = 72 复制代码
job_state = complete_job job_state = job_state ^ complete_job; //job_state & complete_job == 0 job_state = job_state ^ complete_job; //job_state & complete_job == complete_job 复制代码
c#
中你可以这样做。[Flags] enum JobState { New = 1, Assigned = 2, InProgress = 4, Complete = 8, ReOpened = 16, Archived = 32 } 复制代码
if (job_state & JobState.Complete > 0) { //deal with a complete job } 复制代码
=
组合在一起,以便执行带有变量值的操作并赋值给该变量,例如 +=
。如果我们从上面的“异或”例子,我们可以看到它可以更简洁。job_state = complete_job job_state ^= complete_job; //job_state & complete_job == 0 job_state ^= complete_job; //job_state & complete_job == complete_job 复制代码