进制转换完全指南:从整数到小数的原理与精度陷阱

发布时间:2026/10/1 23:53:55
进制转换完全指南:从整数到小数的原理与精度陷阱 前阵子调一个传感器模块串口吐出来一串0x3F 9A F1这样的十六进制字节要还原成十进制浮点数跟示波器读数对比。手边没有转换工具我只好在便签纸上从二进制位权开始一步步摊开算。那时候我突然意识到一个很要命的问题很多人把除2取余乘2取整背得滚瓜烂熟却根本说不清为什么这些办法能凑出正确答案更别提遇到小数、精度、舍入直接两眼一黑。这篇文章就是想把二、八、十、十六进制之间的相互转换彻底讲透重点是小数的处理。我不打算只给口诀而是把每一步背后的原理掰开揉碎让你以后不靠工具也能手算更重要的是知道什么时候该舍入、什么情况下必须警惕精度问题。无论你是刚学计算机基础的学生、准备笔试面试的求职者还是日常要跟协议、字节流、地址打交道的嵌入式或后端开发这篇文章都能当一份随时翻的实操手册。1. 进制到底是什么一套逢几进一的计数规则很多人对进制恐惧是因为一开始就把二进制十六进制当成了某种高深的东西。其实进制就是一个计数规则规则非常简单你打算数到几就进一位。十进制是逢十进一二进制是逢二进一八进制是逢八进一十六进制是逢十六进一。1.1 从数苹果说起为什么十进制对我们如此自然我们现在默认用十进制大概率是因为人类有十根手指。远古时候数羊、数粮食用手指最方便。十个手指用完记一个十然后重新开始这就是逢十进一。所以十进制的数字符号只有 0 到 9 这十个超过 9 就要进位。二进制呢只有 0 和 1 两个符号。数到 1 之后下个数只能写成 10读作一零不是十再下一个是 11然后是 100。八进制有 0 到 7 八个符号数字 8 在八进制里要写成 10。十六进制更狠0 到 9 不够用还要借 A 到 F 来表示 10 到 15所以十六进制里 16 这个数要写成 10。这套规则反过来也成立R 进制的数字每一位只能是 0 到 R-1。你看到18这个写法在八进制里是不可能出现的因为 8 不是合法的八进制数字。1.2 位置计数法每一位自带面额为什么365这个数字能表示三百六十五因为它在十进制里被拆成了 3×100 6×10 5×1。注意从右往左每一位的面额依次是 1、10、100、1000也就是 10 的 0 次方、1 次方、2 次方、3 次方。这种由于数字所在位置不同而代表不同大小的方法叫位置计数法。我特别喜欢用一个类比来理解十进制相当于你钱包里有 1 元、10 元、100 元、1000 元面额的纸币凑任何金额都靠这些面额组合二进制则相当于你的钱包里只有 1 元、2 元、4 元、8 元、16 元这些二进制面额。比如要凑 13 元在二进制钱包里怎么凑8 元加 4 元加 1 元2 元不要也就是 1011写成二进制就是1101。这个类比在后面讲按权展开时会救你一命因为它直接揭示了位置计数法的本质每一位数字乘上它所在位置的权值然后全部加起来。理解了这一点你其实已经掌握了一半的进制转换。剩下的所有算法都是在回答同一个问题给定一个数怎么用指定面额的纸币恰好凑出来。2. 为什么计算机世界里二、八、十六进制总是抱团出现你可能会好奇计算机里面明明只有二进制为什么还要折腾出八进制和十六进制直接说结论八进制和十六进制就是二进制的缩写形式因为 8 是 2 的三次方16 是 2 的四次方一位八进制恰好等于三位二进制一位十六进制恰好等于四位二进制。这个倍数关系让它们之间的转换变得极其简单不需要任何乘除法只要分组或者展开。2.1 二进制的物理基础和八进制、十六进制的缩写本质计算机底层是一个个晶体管物理上最容易稳定表达的是两种状态高电平和低电平也就是开和关。用 0 和 1 来表示这两种状态最可靠抗干扰能力最强。所以计算机内部的一切信息最终都要变成二进制。但二进制有个致命缺点太长。一个十进制数 255 在二进制里写成11111111八位已经有点费眼一个普通的 int 整数是 32 位真要你盯着一串 32 个 0 和 1 去核对谁都会崩溃。这时候八进制和十六进制就派上用场了。八进制可以看成三个二进制位打包成一个符号十六进制则是四个二进制位打包成一个符号。一个字节是 8 位正好可以表示成两个十六进制位比如1010 1011写成AB。所以你在 Wireshark 里看网络包、在 HxD 这类十六进制编辑器里看内存和固件、在协议文档里看寄存器定义看到的全都是十六进制而不是一堆二进制。因为十六进制在紧凑和可读之间找到了最佳平衡点。我说个比较生活化的例子前端调色板里的十六进制颜色码#F5F5F5本质上就是把红、绿、蓝三通道各自的 0 到 255 用两位十六进制写出来了。你平时看到的高级灰、陶土白这些颜色背后都是十六进制转十进制的换算只是浏览器帮你完成了。2.2 位运算里的天然亲和2的幂带来的分组妙处历史上八进制也曾经很流行早期的一些计算机系统甚至默认用八进制查看和输入数据。但后来大家发现一个字节有 8 位用八进制去表示一个字节要变成三位数比较尴尬而十六进制两位代码就能顶一个字节所以十六进制在工程实践中逐渐占据了王座。不管用八进制还是十六进制核心都是它们跟二进制之间的幂关系。这种关系带来的最大便利是二进制转八进制/十六进制完全不用算只要按位数分组。三位一组就是八进制四位一组就是十六进制。反过来一位八进制展开成三位二进制一位十六进制展开成四位二进制这就是正则搜索里经常提到的扩展法也叫一位展开法。顺带提一个容易踩坑的工程细节很多人在 C 语言里想在二进制内存块中查找某个特征串下意识会调用strstr()。但strstr()是文本函数遇到\0就会停止根本不适合处理含有 0x00 字节的二进制数据。正规做法是用memmem()或者自己写循环逐字节比较。十进制、十六进制、二进制本身就是同一种数据的不同皮肤你面对二进制内存时要时刻记住这个区别。3. 一张0~15对照表加上三个记忆锚点我见过不少同学在考试现场临时推十六进制字母表从 10 推到 15 还行一到 11 就卡壳。其实转换做得快不快很大程度取决于你对 0 到 15 这些基础值是否足够敏感。别偷懒把这张表刻进脑子里后面所有转换都会快一大截。十进制二进制八进制十六进制00000001000111200102230011334010044501015560110667011177810001089100111910101012A11101113B12110014C13110115D14111016E15111117F3.1 记忆锚点一16 2的4次方所以四位二进制对应一位十六进制这张表里真正需要背的核心锚点是四位的二进制数。从 0000 到 1111恰好对应 0 到 15。你没必要逐行死记只要记住四位二进制的位权分别是 8、4、2、1然后碰到任何四位二进制就心算加法。比如1010就是 8 加 2 等于 10于是等于十六进制的 A。1101是 8 加 4 加 1 等于 13等于 D。这个8421技巧是秒算十六进制的基础熟到你一看1110就能脱口而出 E 的程度。3.2 记忆锚点二一位八进制等于三位二进制位权是4、2、1八进制的核心锚点是三位二进制位权是 4、2、1。比如八进制 5拆成三位二进制就是 41所以是101。三位最大是111等于 7正好是八进制的最大数字。用同样的逻辑八进制 7 就是111八进制 3 就是011。3.3 记忆锚点三一个字节等于两个十六进制位一个字节是 8 位写成十六进制永远是两位。比如十进制 255 的二进制是11111111十六进制是FF。平时看内存、看协议、看寄存器只要看到两位十六进制马上要反射出这是一个字节。这三个锚点其实是同一件事的三副面孔本质还是 2 的幂。你记住它们之后二、八、十六进制之间的互转就不再是查表题而是看一眼就能写出来的条件反射。4. 整数转换的两种思路短除法与权值展开法整数转换是进制转换的主干我能讲出三种常用方法十进制到 R 进制用短除法R 进制到十进制用按权展开法二进制到八进制/十六进制用分组法。每一种都有它存在的理由下面先说为什么短除法能成立。4.1 十进制转二进制除2取余到底在除什么以十进制 13 转二进制为例标准操作是不断除以 2 取余数13 ÷ 2 6 余 16 ÷ 2 3 余 03 ÷ 2 1 余 11 ÷ 2 0 余 1然后把余数从下往上读得到1101。网上教程都这么写但几乎没有教程解释为什么非要从下往上读。我的理解是这样的十进制数 13 要写成二进制一定是a×8 b×4 c×2 d×1的形式其中 a、b、c、d 各是 0 或 1。这个式子里除了 d×1 这一项其他所有项都能被 2 整除。所以用 13 除以 2余数就是二进制最低位的 d因为只有最低位会漏出来。商是 6它是除去最低位之后剩下的高位部分然后再继续除以 2就能剥离出第二低位以此类推。整个过程就像一层一层剥洋葱从最外层剥到最里层所以读余数的时候要反着读。同样的逻辑可以推广到任意进制十进制转八进制就除以 8 取余十进制转十六进制就除以 16 取余。道理完全一样只是面额变了。如果你忘了余数正反有个极其简单的校验方法十进制奇数转二进制最低位一定是 1偶数最低位一定是 0。比如 13 是奇数那二进制结尾必须是 1你答1011和1101到底哪个对看最低位立刻能淘汰一个。4.2 二进制转十进制按权展开是在算总账反过来二进制转十进制最直接的方法就是按权展开。1101从右往左的权值是 1、2、4、8所以1×8 1×4 0×2 1×1 13这个计算其实就是第一节里钱包面额的复现每一位数字乘上它所在的位权全部加起来就是总数。八进制转十进制也一样只不过位权变成 8 的幂八进制266等于 2×64 6×8 6×1 182。十六进制转十进制就是 16 的幂0xB6等于 11×16 6×1 182。这里有一个很实用的防错习惯从右往左给每一位编号 0、1、2、3那么第 n 位对应的权值就是基数 R 的 n 次方。一位一位写出来再相加比心算稳得多尤其是十六进制还带 A-F最容易在半路算乱。4.3 二进制与八进制、十六进制互转三位一组、四位一组整数二进制转八进制规则是从小数点往左对整数就是往左每三位一组不足三位在左边补 0然后把每组直接换成八进制数字。比如10100110从右往左分组是10 100 110左起第一组只有两位补 0 变成010于是得到010 100 110对应八进制246。注意组内顺序不要搞反010还是十进制 2不是二进制的 10。二进制转十六进制类似只是每四位一组。10100110从右往左分组是1010 0110对应A6。这里有一个细节分组的方向总是从小数点出发。整数部分从右往左分小数部分从左往右分这是最容易出错的地方后面讲小数时还会再强调。反向操作就是扩展法八进制每一位展开成三位二进制十六进制每一位展开成四位二进制。比如十六进制A6A 展开成10106 展开成0110拼起来就是10100110。你可能会问6 明明是110为什么要写成0110因为每一位十六进制必须占满四位少了的位要补零这跟整数分组时左边补零是同一个道理。4.4 八进制/十六进制与十进制的互转聪明的做法是借路十进制和八进制/十六进制直接互转当然是除 8/乘 8、除 16/乘 16。但我实际做题时的习惯是永远先过一遍二进制因为二进制的分组和展开几乎不需要计算比直接除 8 除 16 快得多也不容易错。比如十进制 182 转十六进制直接辗转相除 16 也行但我总会先转二进制10110110再四位一组得到B6。下面这张速查表是我自己贴在手边的核心思路就是能分组就不运算转换方向首选方法原理十进制 → 二进制除2取余逆序读剥离最低位二进制 → 十进制按权展开每一位乘以自己的位权二进制 ↔ 八进制三位分组/展开8 2^3二进制 ↔ 十六进制四位分组/展开16 2^4八进制 ↔ 十六进制先转二进制再分组借路最省事十进制 ↔ 八/十六进制转成二进制中间过渡减少计算量这套组合拳打下来整数转换基本不会再出错。5. 小数转换乘2取整法与无限循环陷阱小数部分才是进制转换真正的分水岭。很多人整数转换很熟练一到小数就犯晕因为小数转换不是剥洋葱而是往外抽牌。但只要理解了它的原理一切就都顺理成章了。5.1 十进制小数转二进制乘2取整的原理以十进制 0.625 转二进制为例标准算法是不断乘 2 取整数部分0.625 × 2 1.25取出整数 10.25 × 2 0.5取出整数 00.5 × 2 1.0取出整数 1把取出的整数按顺序写下来得到0.101于是 0.625 的二进制就是0.101。为什么这么操作因为一个二进制小数的每一位都代表 1/2、1/4、1/8……。当我们把小数值乘以 2 时小数点右移一位原来 1/2 的位置变成了个位这个值是否大于等于 1正好暴露了第一位小数是 0 还是 1。取出这位后把整数部分减掉剩下的部分继续右移就能依次抽出后面的小数位。这就是乘 2 取整的原理和整数转换的除 2 取余恰好互为镜像。这里有一个初学者最容易犯的错整数部分要逆序读余数而小数部分要正序读取整数位。千万不要搞混。记忆技巧也很简单整数转换是从整数里往外剥剥出来的是从低位到高位所以要倒着读小数转换是从小数里往外抽第一位抽出来的就是小数点后面的第一位所以按顺序写就行。5.2 二进制小数转十进制负幂展开二进制小数转十进制是整数按权展开的延伸只不过位权从1/2开始小数点后第一位是 2 的 -1 次方第二位是 2 的 -2 次方以此类推。比如0.1011×0.5 0×0.25 1×0.125 0.625这就是为什么我用0.625做示例它恰好是 5/8分母只有 2 这个因子所以能精确表示。遇到这类分母是 2 的幂的数二进制小数一定是有限位算起来非常舒服。但遇到 0.1、0.2、0.3 这类数麻烦就来了。5.3 带小数的八进制、十六进制互转从小数点开始分组二进制小数转八进制或十六进制方法和整数部分完全一样但方向要特别注意整数部分从小数点往左分组小数部分从小数点往右分组。比如二进制10110.101101转八进制时整数部分往左三位一组10 110补零后是010 110对应 2 和 6小数部分往右三位一组101 101对应 5 和 5所以结果是26.55八进制。转十六进制就四位一组整数部分0001 0110对应 1 和 6小数部分1011 0100对应 B 和 4结果是16.B4十六进制。如果小数部分最后一组不足三位或四位要在右侧补零不是左侧。比如二进制0.1转八进制小数部分只有一位要补成100才是 4而不是从左补成001等于 1。这个细节我至少见过十个新手栽过方向错直接导致整题崩溃。反向操作同样清晰十六进制或八进制小数转二进制直接把每一位展开成四位或三位二进制即可。比如0.B4展开成0.1011 0100也就是0.101101。因为小数右侧补零不影响大小所以展开后末尾多余的话可以直接去掉不影响精度判断。5.4 十进制小数为什么会算不完质因数视角十进制的有限小数在二进制里却可能是无限循环这不是巧合而是一个数学上的必然。十进制的一个有限小数本质上可以写成一个分数分母只能是 2 和 5 的幂相乘因为 10 2 × 5。比如 0.625 5/8 5/(2³)0.2 1/5。而二进制有限小数要求分母只能是 2 的幂比如 1/4、3/8。如果一个十进制小数的分母约分后还残存 5 这个因子它就不可能用有限位二进制精确表示。这就是为什么 0.2 转二进制会无限循环0.2 1/5分母里有 5二进制表示不出来。动手算一下0.2 × 2 0.4取 00.4 × 2 0.8取 00.8 × 2 1.6取 10.6 × 2 1.2取 10.2 × 2 0.4取 0可以看到从第一步开始循环结果是0.001100110011...永远写不完。这为下一章的精度问题埋下了伏笔。6. 精度限制与舍入策略0.1 为什么在计算机里不干净如果你做过前端或者写过一点浮点运算大概率见过这个名场面0.1 0.2的结果不是 0.3而是0.30000000000000004。很多人第一次看到直接怀疑人生。其实根因就是上一节讲的0.1 和 0.2 在二进制里都不是有限小数存储时只能截断或舍入到一个近似值再加起来自然带着误差。6.1 有限位二进制小数只能表示哪些十进制小数如果限制二进制小数只有 n 位那它能表示的值一共只有 2 的 n 次方种可能从 0 到 (2^n - 1)/2^n 的等间距网格。以 3 位二进制小数为例能精确表示的值只有 0、0.125、0.25、0.375、0.5、0.625、0.75、0.875 这八个点。任何十进制小数如果落在这八个点之间就只能选一个最近的点来近似。这就是十进制小数转换为二进制有精度限制时需要考虑舍入吗这个问题的答案不只是需要考虑而是必然需要。如果直接截断误差会系统性地偏向零多次运算后误差会累积得比较难看。所以工程上做量化、定点数设计时第一步永远是搞清楚我要多少位二进制小数允许的最大误差是多少目标值是不是恰好落在可表示的网格点上这些问题想清楚之前盲目写代码是不行的。6.2 实际工程中的舍入四舍五入、向上/向下取整与就近偶数舍入常见的小数舍入策略有四种它们的适用场景差别很大策略规则典型场景向上取整无论如何都往大取资源分配、缓冲区大小计算向下取整无论如何都往小取截断、取整索引四舍五入大于等于一半进一位日常计算、笔试题目就近偶数舍入恰好一半时取偶数IEEE 754 浮点默认最后一种就近偶数舍入最容易被忽略但它恰恰是 IEEE 754 浮点默认的舍入规则。它和四舍五入的唯一区别出现在恰好是一半的情况下0.5这个值要舍入到整数时四舍五入是 1就近偶数舍入是 0因为 0 是偶数。这样做的目的是避免大量数据在统计上产生系统性偏差。不过大家平时做题、手算时绝大部分场景用0 舍 1 入就够了。如果题目要求保留到小数点后 k 位二进制位你算到 k1 位看这一位是 0 还是 1决定要不要进位。比如 0.2 的二进制展开是0.001100110...保留到小数点后 4 位第五位是 1所以要进位结果约等于0.0100对应十进制 0.25。这个近似误差有多大自己一算就有数了。6.3 写在代码前什么时候必须警惕精度问题我总结了几条实战中摸出来的经验供你参考别用直接比较浮点数。要比较就设一个很小的误差阈值比如abs(a - b) 1e-9。涉及金额的选十进制运算用字符串存取、用整数分单位、用定点小数别用原生二进制浮点。嵌入式里传输浮点时要么用 IEEE 754 的原始字节要么约定一个定点格式比如放大 1000 倍转成整数传输明确小数位数。做量化和信号处理时每次舍入都会引入噪声一圈算法跑下来误差可能被放大必要时用更高精度的中间变量最后再舍入一次。理解了这些你就明白为什么数据结构、计算机网络、计算机组成原理里反复强调进制转换它不是一道考试题而是计算机底层所有数值行为的基础。7. 手算辅助用系统计算器与Python验证转换结果进制转换这种东西算得快不算本事算完能验证、能确认自己没算错才是真本事。我每次手算完都喜欢用工具反查一遍。下面提供两个最顺手的验证方式。7.1 Windows计算器程序员模式与网页工具Windows 自带的计算器切换到程序员模式能看到 HEX、DEC、OCT、BIN 四个单选按钮点击就能在十六进制、十进制、八进制、二进制之间切换显示。输入一个数再点不同进制立刻得到结果。这个模式适合验证整数转换但不支持小数算小数还是得用别的工具。手机上的计算器一般没有这个功能直接搜在线进制转换工具也能顶一下但注意选支持小数位数的很多网页工具只会帮你算整数部分。7.2 Python内置函数快速验证整数与浮点数Python 是调试时最顺手的验证工具。整数的进制转换有现成函数# 十进制 182 转二进制、八进制、十六进制 print(bin(182)) # 0b10110110 print(oct(182)) # 0o266 print(hex(182)) # 0xb6 # 字符串按指定进制解析回十进制 print(int(10110110, 2)) # 182 print(int(266, 8)) # 182 print(int(b6, 16)) # 182浮点数可以用float.hex()查看它在内存里的二进制表示但这玩意儿显示出来是十六进制需要你自己脑补成二进制。如果你想直观地看一个十进制小数的二进制展开直接用 Python 循环模拟乘 2 取整最直观def dec_frac_to_bin(frac, places12): if frac 0 or frac 1: raise ValueError(请输入(0,1)之间的小数) digits [] for _ in range(places): frac * 2 if frac 1: digits.append(1) frac - 1 else: digits.append(0) if frac 0: # 完全精确表示可以提前停 break return 0. .join(digits) print(dec_frac_to_bin(0.625, places12)) # 0.101 print(dec_frac_to_bin(0.2, places12)) # 0.001100110011这个脚本哪怕你不想记临时在文件里写几行也能用。需要注意的是Python 浮点数本身不是精确的十进制小数所以当你拿 0.2 去算时实际上是拿它的二进制浮点近似值在算出来的结果跟手算的循环序列可能末尾差一位。这恰好又印证了第六章的论点浮点世界没有真正的 0.2只有离 0.2 最近的二进制近似值。7.3 一个能快速检查整数转换是否写反的笨办法最后分享一个土办法手算完任何整数进制转换后随手做一个最低位校验。十进制数转二进制最低位必须等于十进制数除以 2 的余数转八进制最低位必须等于十进制数除以 8 的余数转十六进制最低位必须等于十进制数除以 16 的余数。我见过太多人把余数顺序写反最后答案多一位少一位。这个校验十秒钟都不用能省掉整个答案被老师画叉的悲剧。我在实际带项目时还有一个习惯只要涉及进制转换的代码每个转换函数都写单元测试测试用例就选 0、1、7、8、15、16、255、256 这种边界值。这些数在进制切换时最容易暴露问题尤其是 8、15、16 这种刚好卡在进位边界的数。你把这个测试跑一遍比我口头给你讲一百句注意别搞反都管用。教过不少新人我发现最容易翻车的始终是那两个地方整数除 2 取余之后把余数顺着读小数乘 2 取整的时候没想清楚要保留多少位就直接写满。其实进制转换这门手艺理解了原理之后剩下的就是大量重复形成条件反射。我的建议是手边常备那张 0 到 15 的对照表加上 Python 脚本辅助验证遇到不确定就写个循环验一下。等你哪天看到0xB6下意识就知道是 182看到37.5能直觉反应出二进制100101.1这份基本功才算真正长在你身上了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询