
第十五夜 · 一门别扭的外语回信来得很快。天真的我读完比特王国的故事只在信末问了一句。你说王国偏要用一门别扭的外语做门牌可这种外语究竟是一种什么文字又为什么偏偏是它。6 他认识4 他也认识可它们并排站在一起他就不认识了。问得好。这是一种外语乍一看也确实别扭。王国里只有一个人把它说得最熟速记官。这一夜我带你进房卡科见一见他再把这门外语的字母表一枚一枚摆给你看。速记官的柜台后面挂着全王国的地图。地图上没有山河只有一条长街。上一夜我说它满街是灯今天不妨说得更直一些满街都是字节一个字节八位每一位不是 0 就是 1。王国的官方文字就是这些 0 和 1每个数字的户口记的都是它的二进制。这种文字有一个好处数怎么存它怎么写永不失真。它也有一个难处太长。客人 33554432 走进房卡科它的二进制写出来足足三十二位速记官要一个字符一个字符往房卡上抄卡片再长也不够用。有人出主意何必抄这些直接把数字本身写上去不就行了。33554432八个字符写得下。速记官把 33554432 写在柜台上反问那个出主意的人你盯着它告诉我它的二进制哪几位是 1。那人答不上来。十进制是给人看的文字看一眼你知道这个数有多大可你一个 1 也看不见想从十进制还原二进制得做一长串的除法和余数房卡科门口的队要排到长街尽头。速记官的办法刻在他那台戳机的把手上只有一行小字。十六等于二的四次方。四个二进制位恰好配一枚戳。四位从 0000 到 1111一共十六种组合从 0 排到 15。给每一种组合铸一枚戳前十个借用你认识的数字0 到 9后六个借字母表开头六个字母a、b、c、d、e、f各自顶一个十到十五。这十六枚戳就是这门外语的全部字母表。翻译从此变成了抄写。把二进制四个一组切开每组照表查一枚戳一枚一枚排下去。100 的二进制是 01100100切成 0110 和 0100 两组查表0110 是 60100 是 4房卡写下 64。一个字节八位恰好两张卡。你念反了也行房卡 64逐枚查回去6 是 01104 是 0100二进制原样回来。全程不做一道算术。速记官的工作其实是抄写他甚至不太会心算。这门外语还有一位老前辈。老前辈用八枚戳三个二进制位配一枚。王国的老房子年代一间房不是八位有十二位一间的有三十六位一间的三是都能整除的老前辈的日子过得很滋润。后来全王国的房子统一成八位一间八被三除不尽老前辈的戳怎么也对不上新房的窗户只好退休。他的字迹至今没有走远。一些老语言里留着他的戳数字开头带一个 0 的写法就是他在敲门门禁系统里也有他三个二进制位管一扇门能不能看能不能改能不能执行三位一枚戳所以你偶尔会在配置文件里遇见 755 这样三个数字。天真的我读到这里你大概要替十进制喊冤它被赶出王国了吗。没有。它只是不再一个人撑场面了。王国的柜台上有三块屏幕。第一块是十进制屏。数字有户口的时候它上岗。所谓户口是这个数说好了按几个字节读、从哪头读、有没有正负。户口在册屏幕写 100你一眼知道这个数有多大人读着舒服。第二块是速记屏。数字没有户口的岗位上岗的是它。把长街原文直接摊开给你看的那个窗口机器与机器之间往来的信一行一行排开的字节这些字节不知道自己该按什么读。十进制屏在这样的位置一声不吭因为它上岗前必须先押注押这几个字节是一家押从哪一头读起押有没有正负。押对了叫展示押错了叫说谎。速记屏不用押一个字节两枚戳逐格直抄位是什么它写什么只摆事实不做解释。第三块屏幕最有趣它证明谁也取代不了谁。街上有一排四间房二进制是 01100100 后面跟着二十四个 0房卡写的是 64 00 00 00。给这排房押上整数户口四间房算一家人屏幕写 100。只把头一间房请出来押上字符户口屏幕写出一个字母 d字符世界的第 100 号恰好是它。同一个字两副户口两种读法都真。十进制屏只能押一种押了 100那个 d 就永远隐身。速记屏呢把 64 00 00 00 摆在台面上两种读法全留给看的人。所以程序员的屏幕上总有那么一个窗口满眼是字母混着数字不是他们偏爱这门外语是那个窗口展示的是长街原文原文没有户口只有 0 和 1。口说无凭我们把三块屏幕请进同一个程序让客人 100 和 33554432 各走一遍。publicclassNight15ThreeScreens{publicstaticvoidmain(String[] args){// 速记官的柜台同一位客人三块屏幕各显各的int[]guests{100,33554432};for(intv : guests) { System.out.println(guest v);// 户口在册十进制屏照常营业System.out.println( decimal screen : v);// 速记屏四位一枚戳逐格直抄一个字节恰好两枚System.out.println( shorthand card : String.format(%08X, v));// 二进制三十二位一位不落System.out.println( binary : String.format(%32s, Integer.toBinaryString(v)).replace( ,0)); }// 头一间房单独请出来换一副字符户口再读一次System.out.println(first room of 100, read as a letter: (char)100); } }实跑输出如下。guest 100 decimal screen : 100 shorthand card : 00000064 binary : 00000000000000000000000001100100 guest 33554432 decimal screen : 33554432 shorthand card : 02000000 binary : 00000010000000000000000000000000 first room of 100, read as a letter: d你不必去数那三十二位速记屏的八个字符替你数了。33554432 的房卡 02000000八枚戳里只有一枚 2其余七枚全是 0客人的全部家当一目了然。这枚 2 藏在哪十进制屏的 33554432 里半点也看不出来速记屏一眼就给你。信写到这里天真的我又追了一句FF 是什么255 还是 −1。速记官摊手两可。他只管记号不管读法。同一个字节 FF户口上写着不分正负八位全是家当读 255户口上写着分正负另有一套读法读 −1。这场官司从头到尾是户口的跟速记官无关他连笔都不用换。不过他确实有一手别人比不了的本事。FF FF FF FF四间房的房卡一字排开读一间是 FF读两间是 FF FF读四间还是 FF FF FF FF一格一格的读法全摆在明面上想看哪层看哪层。换成十进制 4294967295你只看得见四间房的总数低处每间房里住着什么从数字里半点也看不出来。懂行的人还能多看一眼分正负的 −128写在卡上是 FFFFFF80六枚 F 顶着二十四个 1那是补齐的符号最后两枚戳 80才是 −128 的本尊。这些层次十进制屏统统不给。最后的考题来了。天真的我问既然十六行那二十为什么不行二十不是更大吗更大不是更省字符吗。速记官的考题从来只考一样。把候选的底写成二进制数一数 1 的个数。十六10000一个 1。十1010两个 1。十二1100也是两个 1。区别就在这里。底若只有一个 1四个一组的刀切下去刀刀落在空隙上组组整齐底若有两个 1怎么切都错位。二十的二进制是 10100两个 1一样切不齐。字符省得再多戳对不上位这门外语就只是乱码。这一刀机器一句话就能试出来。publicclassNight15SingleOne{publicstaticvoidmain(String[] args){// 速记官的考题二进制里只有一个 1 的数才配当这门外语的底int[]candidates{16,10,12,32};for(intn : candidates) {// n 与 n-1 相与恰好消去最右边的一个 1// 消一轮就全零说明原本只有这一个 1否则二进制里还有别的 1booleanfit(n (n -1)) 0; System.out.println(n binary Integer.toBinaryString(n) fit fit); } } }实跑输出如下。16 binary 10000 fit true 10 binary 1010 fit false 12 binary 1100 fit false 32 binary 100000 fit true十六和三十二过闸十和十二留在门外。判据简单到不像学问可它就是这门外语的国籍法。信的最后天真的我终于问出了我最想让他问的那句。那我们的幻方呢它也住在这条街上。住着呢。幻方进城也要验档验的也是 0 和 1只是验法另有一套。速记官看整个数幻方看二进制的末尾。把阶数写成二进制数一数末尾连着几个 0。3、5、7 写成二进制末尾连一个 0 都没有这是一档奇数阶。10 的二进制是 1010末尾连着一个 0这是第二档。4、8、16末尾至少连着两个 0这是第三档双偶阶。你还记得第十三夜那部 289 行的手稿奇数阶一套步法走遍双偶阶对称倒放就成唯独第二档动用四分块拼装篇幅最长那是瑛姑没考的题。现在你知道它为什么难了末尾恰好连一个 0 的阶数正是幻方里最难伺候的客人。速记官在旁边听完补了一句他的版本。他的考题更苛刻不看末尾看整个数的二进制里是不是只有一个 1。10 在幻方籍上是第二档的正牌居民到他这儿过不了闸1010两个 1。16 更妙它在幻方籍上住在第三档跟 4 和 8 是街坊到他这儿却是上宾10000全数只此一个 1。同一个数两本户籍量的都是 0 和 1尺子不一样。所以这一夜的判词只有一句。一个数能不能当这门外语的底不看它在幻方籍上住哪一档只看它的二进制里是不是只有一个 1。十进制没有不好它只是在 0 和 1 的世界里永远切不齐。幻方旅行团还睡在长街上房卡 08、01、06一字排开你现在读得懂了。这门外语一点也不别扭它只是不肯拣好听的说。学会它不是为了跟机器套近乎是为了有一天能亲眼看见数字睡着的样子。① 真伪核对逐条照实登记。Ⅰ 十六记号 十六进制使用 0 至 9 与 a 至 f 共十六个符号a 至 f 依次表示十至十五一枚戳对应四位二进制一个字节8 位恰用两枚戳真通行记数规范。Ⅱ 八进制与老房子 PDP-8 字长 12 位分四组、每组 3 位以八进制记地址与数据前面板指示灯三位一组真已联网核查同期机器字长 12、18、24、36 位均能被 3 整除八进制与之相配真已联网核查字节统一为 8 位后8 不能被 3 整除八进制错位出局十六进制一字节两枚戳接位真算术。C 语言保留八进制字面量前导 0如 0123与八进制转义如 \121是老前辈的化石真语言规范。Ⅲ 门禁里的八进制 Unix 文件权限每 3 位一组读、写、执行一枚八进制数字记一组755 的 7 是三位全 1能读、能写、能执行5 是两个 1能读、能执行真。Ⅳ 三块屏幕 调试器对已声明的变量默认按十进制显示内存窗口与十六进制查看器不问类型、按字节以十六进制显示真通行行为。字符世界第 100 号为 d真第十四夜核对在案ASCII。Ⅴ 同一字节两读 一字节 FF按无符号读 255按 8 位补码有符号读 −132 位有符号 −128 写作 FFFFFF80前六枚 F 为 24 位符号扩展真算术与补码定义。Ⅵ 判据 n 与 n−1 按位与为零当且仅当 n 是 2 的整数次幂且 n 大于 0真实跑验证。用数学话说速记官的国籍法是底的质因数必须只有 2$10 2 \times 5$坏在那个 5幻方验档数的是阶数里因子 2 的含量恰为二进制末尾连着的 0 的个数真算术。Ⅶ 幻方三分类 奇数阶、单偶阶$4k2$、双偶阶$4k$三档完备4、8、16 为正牌幻方阶居双偶档二阶幻方无解是唯一缺席的 2 的幂真第十三夜勘误在案。第十三夜手稿 289 行、单偶阶段篇幅最长真实跑在案。Ⅷ 演示件 Night15ThreeScreens 与 Night15SingleOne 实跑输出与文中一致真JDK 21 实跑验证。② 术语对照卡家常名与学名对照定义不含比喻。这些家常名只是记号机器里没有官员也没有纸卡只有约定。Ⅰ 长街学名内存。按字节编址的存储序列每字节 8 位。Ⅱ 戳与速记学名十六进制。以 16 为基数的记数法使用 0 至 9 与 a 至 f每一位对应二进制四位。Ⅲ 外语的底学名基数。记数法使用的符号个数有 2、8、10、16 等几进制即以几为基数。Ⅳ 户口学名数据类型。约定一段数据按几个字节一组、从哪一端开始读、是否带符号的规则。Ⅴ 位与字节。位是二进制的一个数位非 0 即 1字节是 8 位一组是内存的最小编址单位。③ 今夜习题三道评论区见。Ⅰ 2026 的房卡。上一夜的习题你写过它的二进制这一夜给它办房卡。四位一组切二进制写出速记写法再逐枚戳对回四位自查一遍。Ⅱ 只有一个 1 的国籍法。64、6、28、96写出各自二进制数一数 1 的个数谁够格当这门外语的底。Ⅲ FF 的两副面孔。一个字节的 FF按不分正负读是 255按分正负读是 −1。这场官司该找谁判速记官管不管说出你的理由。本书目录导航上一篇第十四夜 幻方与奇妙的二进制 下一篇第十六夜 十只白鼠 起点卷首语·算法童话版权声明本文为作者原创受著作权法保护。未经授权禁止转载、搬运、摘编、改编及任何形式的二次创作个人学习引用请注明作者与原文出处。转载授权请联系作者CSDN 私信。侵权必究。