凌晨一点半,你盯着屏幕上一行大佬写的 `flags & ~0x2`,看了二十分钟没看懂。群里弱弱问了一句,对面甩回来四个字:回炉学进制。
别不服气。进制这玩意儿,是所有程序员都被坑惨了的第一课,也是唯一一门“学一次用十年”的底层功夫。今天把这层窗户纸捅破。
二进制:只用 0 和 1 两个状态表示信息,计算机的全部世界观的起点。
先自问一句:计算机为什么不用十进制?搞十档电压,一位就能顶二进制三位多,多优雅?
答案是:电路不答应。要区分 10 个电压档位,每档之间只差零点几伏,一点电磁干扰进来直接把你的 7 读成 8,纯纯翻车。
而两档电平呢?高电平和低电平之间隔着一大段缓冲区,噪声想搞坏它得费老劲了。说人话:灯只有“开/关”最可靠,你做个十档调光器试试?
所以二进制是被物理规律逼出来的选择,不是谁偏心。 别赌运气去研究什么三进制计算机,苏联人上世纪试过了,历史已经给过答案。
十六进制:用 0-9 和 A-F 共 16 个符号计数,程序员写二进制的官方速记。
这里先破一个迷思:十六进制是一门要重新学的新数制吗?不是,它就是二进制的缩写。
数学依据就一条:16 = 2⁴,所以每 4 个二进制位,正好对应 1 个十六进制位。一一对应,机械转换。
举个例子你就懂了:
```text
1111 1111 = FF
0110 0100 = 64
```
对照关系就 16 组:0000 到 1111,分别对应 0 到 F。F 是 15,四个 1 挨着,完事。
为什么程序员离不开它?因为没人想读 32 个 0 和 1。
说人话:十六进制就是给二进制做了个 zip 压缩,内容一点没变,体积缩了 4 倍。 躺赚的速记法,不学白不学。
位运算:直接对二进制位进行操作,绕过十进制的加减乘除,速度快到犯规。
最经典的一条:`n << 1` 等于 `n * 2`。左移就是在右边补零,`0001`(1)左移一位变 `0010`(2),再移变 `0100`(4)。
为什么?因为二进制里每高一位,权重就翻一倍,跟十进制左移一位变乘 10 是一个道理。
那现代编译器这么聪明,乘法早优化成位移了,还有必要学吗?有,两个原因:面试官考,以及你接盘的老代码里全是它。
几个高频黑魔法,见过就不慌:
见过有新人把权限系统设计成十几个 bool 字段的,查询写了一百行。用位掩码一个 int 就装下了,加权限就是按位或一下。这差距,就是懂和不懂进制运算的差距。
前导零:数字前面的 0,在很多语言里代表“这是八进制”,不是好看用的。
经典翻车现场:`chmod 644` 和 `chmod 0644` 在命令行里等价,但在 C、Python 2 里,`0644` 会被解析成八进制的 420,而 `644` 是十进制的六百四十四。差着十万八千里。
JS 里更刺激,老代码 `010` 会被当八进制解析成 8,ES5 之后严格模式直接报错。接盘遗留代码的时候,看到 0 开头的数字先停下来想想,别直接跑。
补码:负数在计算机里的表示法,按位取反再加一,好处是减法器可以复用加法器。
为什么你打印 `0xFFFFFFFF` 有时候是 4294967295,有时候是 -1?因为 32 个 1 这个东西,到底是啥取决于你怎么“看”它。
无符号解释就是 2³² - 1;有符号解释,最高位是符号位,剩下按补码还原,就是 -1。同一个 bit 串,两种人生。
这也是为什么 `0.1 + 0.2 !== 0.3` 不是 bug——十进制的 0.1 在二进制里是无限循环小数,存进去就被截断了。这不是 JavaScript 的锅,是所有浮点实现共同的宿命,IEEE 754 说了算。
自验一下手算练没练明白的时候,别硬刚脑内推,拿 二进制转换工具 对照着验算,手算结果和工具对不上,八成是哪一步位分组错了,比自己干瞪眼快十倍。
Q1:十六进制是不是要重新背一套换算表?
不用。核心就一条“4 位一组”,二进制每 4 位切成一段逐段换,剩下的交给熟练度。
Q2:位运算是上古技术,现在写业务代码用不上吧?
用得上,只是你没意识到。权限掩码、Redis 的 bitmap、协议解析、哈希混淆、前端的颜色运算,底下全是位操作,面试更是年年考。
Q3:0.1 + 0.2 不等于 0.3,是语言设计失误吗?
不是,任何二进制浮点数都存不下十进制的 0.1,这是无限循环小数的天生硬伤。要精确小数就用定点数或 decimal 类型,别赌浮点的运气。
评论列表 (0条)