秘密

秘密

学习练习

简介

二进制位最终直接对应到 CPU 或内存中的晶体管,以及每个晶体管是“开”还是“关”。

底层操作,俗称“bit-twiddling”,在系统级语言中尤为重要。

像 Julia 这样的高级语言通常会抽象掉大部分这类细节。不过,基础语言中提供了完整的位级运算。

注意: 要在 REPL 中看到人类可读的二进制输出,下面几乎所有的示例都需要用bitstring()函数包裹起来。这在视觉上很干扰,因此这个函数的多数出现都被省略了。

位移操作

整数类型,无论有符号还是无符号,都可以表示为由 1 和 0 组成的字符串。

julia> bitstring(UInt8(5))
"00000101"

位移只是把所有内容向左或向右移动指定的位数。对于UInt类型,一些位会从一端掉出去,另一端则用 0 填充:

julia> ux::UInt8 = 5
5

julia> bitstring(ux)
"00000101"

julia> ux << 2 # left by 2
"00010100"

julia> ux >> 1 # right by 1
"00000010"

每次左移都会使值翻倍,每次右移都会使其减半(会有截断)。用十进制表示会更明显:

julia> 3 << 2
12

julia> 24 >> 3
3

这种位移比“正规”的算术运算快得多,因此这种技巧在底层编程中非常流行。

对于有符号整数,我们需要更小心一些。

左移相对简单:

julia> sx = Int8(5)
5

julia> sx # positive integer
"00000101"

julia> sx << 2
"00010100"

julia> -sx # negative integer
"11111011"

julia> -sx << 2
"11101100"

因此,正的有符号整数左移与无符号整数相同。

负值以补码形式存储,也就是说最左边的位是 1。左移没有问题,但右移时,最左边的位该如何填充?

julia> sx >> 2 # simple for positive values!
"00000001"

julia> -sx # negative integer
"11111011"

julia> -sx >> 2 # pad with repeated sign bit
"11111110"

julia> -sx >>> 2 # pad with 0
"00111110"

>>运算符执行算术移位,保留符号位。

>>>运算符执行逻辑移位,用 0 填充,就好像这个数是无符号的。

按位逻辑

在之前的概念中我们了解到,运算符&&(与)、||(或)和!(非)用于布尔值。

还有对应的运算符&(按位与)、|(按位或)和~(波浪号,按位非),用于比较两个整数中的位。

julia> 0b1011 & 0b0010 # bit is 1 in both numbers
"00000010"

julia> 0b1011 | 0b0010 # bit is 1 in at least one number
"00001011"

julia> ~0b1011 # flip all bits
"11110100"

julia> xor(0b1011, 0b0010) # bit is 1 in exactly one number, not both
"00001001"

这里,xor()是异或,以函数的形式使用。

说明

你的朋友刚刚给你发来一条带有重要秘密的消息。 为了不让别人轻易读到它,这条消息经过一系列位操作进行了加密。 你需要编写一些函数来帮助解密这条消息。

1. 将位向右移回去

解密这条消息的第一步,是把加密过程中的移位还原,也就是把位向右移回去。 解密过程中后续的一些步骤会假定 0 是从左侧插入的。

实现shift_back()函数,它接收一个值和要移位的位数,并执行移位。

shift_back(0b1001, 2) # => 0b0010

2. 设置一些位

接下来,有一些位需要设为 1。

实现set_bits()函数,它接收一个值和一个掩码,返回把值中的相应位设为 1 之后的结果。 如果mask中的某一位是 1,那么value中对应的位也应设为 1。 其余所有位保持不变。

set_bits(0b0110, 0b0101) # => 0b0111

3. 翻转特定位

加密过程中有一些位被翻转了。 要解密这条消息,就需要把它们翻转回来。

实现flip_bits()函数,它接收一个值和掩码。 掩码指明值中哪些位需要翻转。 如果mask中的某一位是 1,value中对应的位就会被翻转。 其余所有位保持不变。

flip_bits(0b1100, 0b0101) # => 0b1001

4. 清除特定位

最后,还有一些位解密后总是 0。

实现clear_bits()函数,它接收一个值和掩码。 掩码中为 1 的位,在value中对应的位应设为 0。 其余所有位保持不变。

clear_bits(0b0110, 0b0101) # => 0b0010
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Julia Exercism

准备好开始 秘密 了吗?

注册 Exercism,借助 35 个概念128 个练习 和真人导师指导,学习并掌握 Julia,全部免费。