二进制与十进制互转详解:从位权原理到手算技巧与代码实现 二进制和十进制互转听起来像是大学第一堂计算机导论就讲完的基础课。但入行这些年我的观察是真有把握在笔试现场一两分钟内手算对、在老代码里一眼看穿标志位含义的人比例并不高。“好像会但真算就错”是常态。这篇文章想把这件事彻底讲透互转的底层原理是什么、手算有哪些快方法、常见坑到底怎么避开、代码里怎么实现顺便把我这些年踩过、也看别人踩过的那些“低级错误”一次说清。适合谁看刚学编程的小白准备面试的求职者以及日常要跟底层层代码打交道的开发者。你不用背一堆口诀只要把“位权”这件事吃透后面所有方法都是顺出来的。哪怕你手里有计算器我也建议先把心算的功底练起来因为很多场景根本轮不到你打开计算器。1. 先想清楚这题不是“按计算器”就够的很多人觉得二进制转十进制、十进制转二进制无非就是按一下计算器、调一个内置函数。真到用的时候才发现理解“为什么这么转”和“会不会按按钮”完全是两码事。在笔试、面试或者代码评审现场你往往需要在几秒内给出结果。比如看到一串二进制标志位要立刻知道它代表哪些权限看到一个十进制的端口号要快速换算成网络协议里的十六进制字段甚至在调试嵌入式日志时打印出来的一串 0 和 1你得马上判断出寄存器里的状态位是不是符合预期。这些场景都没有慢悠悠开计算器的时间也没有人允许你掏出手机查在线转换工具。所以这项能力的本质不是“计算结果”而是“建立数字直觉”。一旦你熟悉了二进制的“位权”结构你会发现自己看数字的方式都变了。1.1 所有转换的本质都是“位权”问题先看十进制。为什么 3421 这个数写作 3421因为它是3 × 10³ 4 × 10² 2 × 10¹ 1 × 10⁰每一位上的数字乘上“它所在位置对应的 10 的幂次”再加起来。这个幂次就叫“位权”。十进制从左到右依次是千位、百位、十位、个位对应 10³、10²、10¹、10⁰。二进制完全同理只不过底数从 10 变成了 2。比如二进制数 10111 × 2³ 0 × 2² 1 × 2¹ 1 × 2⁰ 8 0 2 1 11你看只要抓住“位权”二进制转十进制就是一次展开求和十进制转二进制反而是把这个过程倒过来把十进制数拆成若干个 2 的幂次之和。我习惯用“筹码”来打比方。十进制像一套有 1 元、10 元、100 元、1000 元面额的筹码二进制像一套只有 1 元、2 元、4 元、8 元、16 元面额的筹码每种面额要么选一枚要么不选不能出现“半枚”。你手里有 138 元想用二进制的筹码凑出来那就先拿最大面额的 128 元剩下 10 元再拿 8 元剩下 2 元再拿 2 元刚好凑齐。于是你选的筹码是 128、8、2对应二进制位上写 1没选的写 0就得到 10001010。这个类比在实战中非常管用。1.2 真实场景里它到底出现在哪别觉得二进制只在课本里存在实际开发中到处都是。最典型的是 IP 地址和子网掩码。你在配置网络时看到 192.168.1.1看到掩码 255.255.255.0这些十进制数字背后全是二进制。255 就是 8 个 1所以子网掩码表示“前 24 位是网络号”。如果你对二进制互转不敏感排查一个网络问题时就会非常吃力。其次是权限系统。Unix 的 chmod 755看起来是三个十进制数字本质是三组二进制位7 111 表示 rwx5 101 表示 r-x。设一个权限位、判断一个权限位全是位运算和二进制展开。还有编程里的“标志位flag”。很多人用一个 int 同时存十几个开关状态每个位代表一个开关。这时你看到一个数字 132如果能快速意识到 132 128 4 10000100你就知道是第 8 位和第 3 位是开的。这个能力在嵌入式、网络协议、游戏状态机里都是刚需。另外调试浮点数的时候如果你知道 0.1 在二进制里是个循环小数就不会对“0.1 0.2 不等于 0.3”感到意外。基础概念不通很多坑你连原因都找不到。2. 十进制转二进制从“除 2 取余”到“心算”一次讲清十进制整数转二进制主流方法有三套短除法、幂次拆分法、以及针对小数的乘 2 取整法。初学的时候建议先用短除法因为它最机械、最不容易错练熟了再切换到幂次拆分法速度会快很多。2.1 最稳妥的短除法除 2 取余短除法的原理一句话一个十进制数不断除以 2每次除下来的余数是二进制位直到商变成 0最后把余数倒着读。拿 138 举例完整过程是138 ÷ 2 69 余 069 ÷ 2 34 余 134 ÷ 2 17 余 017 ÷ 2 8 余 18 ÷ 2 4 余 04 ÷ 2 2 余 02 ÷ 2 1 余 01 ÷ 2 0 余 1到这里商已经是 0停止。然后把余数从下往上读10001010。所以 138 的二进制是 10001010。为什么必须倒着读因为第一次除 2 得到的余数其实就是二进制的最低位。你可以理解为十进制的个位是“除以 10 后的余数”二进制的个位就是“除以 2 后的余数”。第一次除完个位已经确定继续除剩下的商再去确定更高位。所以过程是从低位往高位算但二进制书写时高位在左边自然要倒序。验证一下10001010 128 8 2 138正确。这个方法的优点是不会漏位适合笔试时打草稿。缺点是稍慢但算多了就熟练了。如果你用 Python 写一个等价实现核心就是循环取余def dec_to_bin(n: int) - str: # n 为 0 时单独处理 if n 0: return 0 bits [] while n 0: bits.append(str(n % 2)) n // 2 # 余数倒序拼起来 return .join(reversed(bits))注意这里n // 2是整除 2。Python 里//是整数除法如果用/就会变成浮点数导致死循环或者结果错误这是我见过很多新手踩的坑。2.2 更快心算幂次拆分法短除法适合打草稿但如果你想心算幂次拆分法更快。首先记住 2 的幂次表至少记到 2 的 10 次方2⁰ 12¹ 22² 42³ 82⁴ 162⁵ 322⁶ 642⁷ 1282⁸ 2562⁹ 5122¹⁰ 1024十进制转二进制就是看这个十进制数由哪些 2 的幂次组成。以 138 为例138 128 1010 8 2所以 138 128 8 2在二进制里选了哪些幂次对应位写 1没选的写 0。从 2⁷ 到 2⁰ 排下来128164032016081402110得到 10001010。这个方法的核心是“从大到小凑数”。先找不超过目标数的最大 2 的幂减掉然后对剩下的部分继续找最大的 2 的幂直到所有部分拆完。中间没凑到的位全部补 0。用这个方法算大数也很快。比如 1000最大 2 的幂是 5121000 - 512 488488 中最大是 256488 - 256 232232 中最大是 128232 - 128 104104 中最大是 64104 - 64 4040 中最大是 3240 - 32 8所以 1000 512 256 128 64 32 8对应二进制 1111101000。可以验证51225612864328 1000没毛病。有个小技巧当你看到一个数不太大时先估算它落在哪个区间。比如 200 大于 128 小于 256说明最高位是第 7 位2⁷需要 8 位二进制表示。这一步会让你心里有底不容易漏位或补错位。2.3 小数部分乘 2 取整法整数用除 2小数恰恰反过来用“乘 2 取整”。十进制小数转二进制时不断把当前小数部分乘 2把所得积的整数位取出来作为二进制小数的每一位然后继续处理剩下的小数部分直到小数部分变成 0或者达到你需要的精度为止。以 0.625 举例0.625 × 2 1.25整数位是 1剩下 0.250.25 × 2 0.5整数位是 0剩下 0.50.5 × 2 1.0整数位是 1剩下 0所以十进制 0.625 的二进制是 0.101。验证一下0.101 1 × 2⁻¹ 0 × 2⁻² 1 × 2⁻³ 0.5 0.125 0.625正确。如果一个小数乘以 2 之后永远除不干净就说明它在二进制里是无限循环小数。典型例子就是 0.10.1 × 2 0.2取 0剩 0.20.2 × 2 0.4取 0剩 0.40.4 × 2 0.8取 0剩 0.80.8 × 2 1.6取 1剩 0.60.6 × 2 1.2取 1剩 0.2……到这里你会发现 0.2 又回来了进入循环。所以在二进制里0.1 是无限循环小数。计算机浮点数只能用有限位去截断这就是为什么你拿 Python 算 0.1 0.2 会得到 0.30000000000000004。需要注意小数的二进制转换往往不是几步就能算完的。做题时如果遇到循环题目一般会要求“保留几位”但实际开发中你需要知道“这个数根本没法精确表示”。这是精度问题的根源不是 Python 的 Bug。3. 二进制转十进制展开、分组、心算技巧从二进制往十进制转换我推荐三个层次最笨但不容易错的位权展开法适合耐下心来算乘 2 加位法适合心算和代码实现8421 分组法适合快速处理长二进制串。3.1 基础版位权展开法这是最标准的做法也是很多人唯一会的方法。把一个二进制数从右往左编号最低位的编号是 0对应的权值是 2⁰往左依次是 2¹、2²、2³……以 101101 为例从右往左标1 × 2⁵320 × 2⁴161 × 2³81 × 2²40 × 2¹01 × 2⁰1加起来32 8 4 1 45。所以二进制 101101 等于十进制 45。这里最容易犯的错是把最低位当作 2¹ 开始算。千万别。最低位就是“个位”对应 2⁰也就是 1。很多人在这一步错一错全错。3.2 提速版乘 2 加位法秦九韶算法位权展开法写出来很直观但位数一长就容易算乱。我有一个更喜欢的心算法从左往右读初始化结果为 0每读到一个位先把当前结果乘以 2如果这一位是 1再加 1。还是 101101第一位 10 × 2 1 1第二位 01 × 2 0 2第三位 12 × 2 1 5第四位 15 × 2 1 11第五位 011 × 2 0 22第六位 122 × 2 1 45得到 45和位权展开一致。这个方法本质上就是“一边乘 2 一边加当前位”跟十进制里从高位往低位读数的直觉完全一致。你读“101101”心里就是在累积 45。这个算法在代码里也特别自然前面 Python 代码就是按这个思路写的。如果你要心算一个 8 位二进制数这个方法比展开全部位权快得多。练熟之后8 位以内的转换基本一两秒内能出结果。3.3 小数部分二进制小数的计算要小心二进制小数转十进制和整数部分一样还是位权展开只是小数点右边的权重是 2 的负次幂。第一位小数对应 2⁻¹也就是 0.5第二位是 2⁻²也就是 0.25第三位是 2⁻³也就是 0.125第四位是 2⁻⁴也就是 0.0625。比如二进制 0.1011 × 0.5 0.50 × 0.25 01 × 0.125 0.125加起来 0.625。小数点右边每增加一位精度就提高一倍但原理还是位权展开。注意不要跟左边写成对称的“2^1, 2^2”左边是正次幂右边是负次幂。很多人把 0.101 算成 5就是因为把右侧小数位也当成了 2¹、2²。我在实践里还有一个经验如果你在协议字段里看到一个二进制小数先把它按整数转换再除以对应的 2 的幂次会更不容易错。比如 0.101 先看“101”等于 5小数点后有 3 位所以除以 2³ 8得到 5/8 0.625。这个“位移”视角对于手算非常友好。4. 手写互转的常见问题与排查方法这一章是重点中的重点。我见过太多人在转换上吃亏而且往往是同一个坑反复踩。下面把典型错误、排查方法和避坑口诀都列清楚。4.1 余数顺序写反这是短除法最容易犯的错除 2 取余算出了余数序列但直接正着读得到的结果正好是倒过来的错误答案。用 138 举例如果正着读余数是 01010001而正确的是 10001010不仅完全不一样甚至位数都可能让人迷糊。排查方法很简单算完立刻反向验证。把得到的结果再按位权展开如果合不回去一定是写反了。口诀就一句“除到商 0 为止余数从下往上读。”第一次除出来的余数一定是二进制最低位所以要放右边最后一次除出来的余数才是最高位要放左边。我在笔试现场看到太多人这里丢分。明明前期思路全对最后写反白算。你可以在草稿纸上把除法的竖式写得整齐一些每次余数写在右边一列最后从下到上抄写能极大减少出错。4.2 位权起点搞错二进制转十进制时最低位是 2⁰不是 2¹。很多人很自然地觉得“第一位就是 2¹”于是 1011 被算成 2 4 8 14而正确是 8 2 1 11。这一错后面全错。我建议的规避方法不要在脑子里数“第几位”而是在纸上从右往左标 0、1、2、3。编号就是指数。比如从右往左第四位编号 3所以权值是 2³。这样就不会出现“第一位是 1 还是 0”的纠结。如果你是从左往右标权值也别标成 2^(n)、2^(n-1) 搞混。把握一个原则最左边的最高位权重最大等于 2^(位数-1)。一个 8 位二进制数最左边那位权重一定是 128最右边那位权重一定是 1。只要把这个基准记住就不会标反。4.3 小数转换的精度幻觉小数转换最大的坑是误以为二进制可以像十进制一样“精确表示所有小数”。事实是很多十进制小数在二进制中是无限循环的。最典型的例子就是 0.1。上面已经演示过0.1 转成二进制后进入循环。这意味着计算机里存的 0.1本身就是近似值。所以类似“为什么 0.1 0.2 不等于 0.3”的问题根源就在这里。处理建议在代码里比较浮点数时不要用“等于”用“差值小于某个极小值”涉及金钱、金额这类需要精确计算的场景别用浮点数用整数以分为单位或者专门的十进制类型如果手算小数转换题目一般会指定保留多少位如果没指定你可以先转几位看看能不能刚好变成 0如果出现循环就要明确说明“无法精确表示”。4.4 负数怎么转补码是避不开的进阶题面试和笔试里整数的正负转换很快会进阶到“负数怎么表示”。这里核心是补码。计算机里整数默认用补码存储原因是可以把减法和负数统一成加法。补码的定义一句话正数的补码就是它本身负数的补码是“对应正数各位取反再加 1”。以 8 位二进制为例求 -33 00000011取反 11111100加 1 11111101所以 -3 在 8 位二进制里是 11111101。如果你按无符号整数展开它是 253但按补码理解最高位是符号位1 表示负数实际值是 -128 64 32 16 8 4 1 -3。这里我特别想强调看到一串 1 开头的二进制先搞清楚它是有符号还是无符号。同样的位模式 11111101无符号是 253有符号是 -3。所以做题时一定要看清题目问的是“无符号整数”还是“有符号补码”。这种题每年都有人挂。还要注意位数问题。8 位二进制能表示的有符号范围是 -128 到 127因此 -138 用 8 位装不下得用 16 位。解题时先确认位宽再做取反加一不然会溢出。5. 从手算到代码互转的几种实现方案手算能力强代码实现也必须能写出来。面试时经常会被要求“不用内置函数实现转换”所以这里给出 Python 和 C 风格的参考写法并说明内置函数的边界。5.1 Python 手写互转函数十进制转二进制前面已经给过除 2 取余的版本重点再说一下“乘 2 加位”转十进制def bin_str_to_dec(bits: str) - int: # 支持带 0b 前缀的字符串 if bits.startswith(0b): bits bits[2:] value 0 for ch in bits: value value * 2 int(ch) return value这个函数的思路就是从左往右扫一遍每读入一位就做一次“乘 2 加当前位”非常像秦九韶算法。它比“逐个展开幂次求和”更快也更少出错。如果你处理的是小数部分可以把小数位单独提取出来做一个函数。比如0.101字符串通过“小数点位置”区分整数部分和小数部分各自转换后再拼接。这类题目考察的是你对位权的理解而不是花哨的语法。5.2 C 语言和嵌入式里更常用的位运算写法实际开发中你很少需要把数字全量转成字符串更多时候是“检查某一位”“设置某一位”“清除某一位”。这时位运算比转换函数更直接。检查第 pos 位是否为 1int bit (num pos) 1;设置第 pos 位为 1num | (1 pos);清除第 pos 位置 0num ~(1 pos);比如你有一个整型变量表示 8 个开关想打开第 3 个开关就执行num | (1 2)。这里的1 2等于 4对应二进制 00000100。你看位运算本身就是二进制的天然表达理解了互转读这种代码会非常顺畅。还有一个面试常见题统计一个整数的二进制表示里有多少个 1。经典解法是 Brian Kernighan 算法int count_ones(unsigned int n) { int count 0; while (n) { n (n - 1); // 每次去掉最低位的 1 count; } return count; }这个技巧的本质是n - 1会把最低位的 1 变成 0同时把它右边的 0 都变成 1n (n-1)就把这位 1 消掉。理解不了也没关系先记住它后面刷题时会反复遇到。5.3 内置函数的边界以及什么时候别用Python 提供了两个很顺手的内置能力bin()和int(str, 2)。# 十进制转二进制字符串 binary_str bin(138) # 0b10001010 # 二进制字符串转十进制 number int(10001010, 2) # 138bin()返回的字符串带0b前缀需要拼接或展示时可以切片去掉int()的第二个参数2表示按二进制解析。这两个函数在日常开发里非常省事。但面试手写代码时考官通常会先问“能不能不用内置函数”。倒不是故意为难你而是想确认你理解原理而不是只会调 API。所以我建议先用内置函数写出快速答案再准备一份手动实现版本。万一面试官追问你能立刻写出上面的 Python 循环就说明你真的懂。另外在一些嵌入式环境和 PLC 编程里内置函数可能不存在或者编译器优化不理想。这时候手动位运算和循环实现就是刚需。掌握原理的人永远不慌。6. 练成“条件反射”的几个习惯互转这件事光看教程没有用必须动手练。下面分享几个我实操下来的习惯对建立数字直觉很有帮助。第一每天花三分钟做几道随机转换。别嫌基础太简单这种基础能力就是靠高频重复建立条件反射。可以自己写一个小脚本随机生成 0 到 255 之间的整数先看二进制写出十进制再看十进制写出二进制。坚持一两周8 位以内的转换基本可以脱口而出。第二把常用的二进制值背成“肌肉记忆”。比如 255 是 11111111128 是 10000000192 是 11000000224 是 11100000240 是 11110000。这些值在子网掩码计算里几乎是天天见背熟了排障效率直接翻倍。第三调试日志里看到二进制位串先别急着用外部工具换算。强迫自己心算一下标志位是什么值、对应哪些开关。刚开始可能慢但练一段时间后你看数字的速度会比开计算器快得多。而且这种能力是“一旦建立就不太会丢”的以后任何底层技术都受益。第四写代码时主动用位运算表达状态。不要总用一堆布尔变量试着用一个整数加几个位标志。比如状态值flags 0b1010要通过flags 0b1000判断某个功能是否开启。这种练习会让你在实战中不断巩固二进制直觉。最后说一个我自己的体会很多人觉得这种基础太简单不值得专门花时间。但当我真正面对那些写出“0.1 0.2 结果不对”的人、面对那些看到0b11111101都反应不过来是无符号 253 的人我才意识到基础概念的牢固程度最终会拉开人与人之间的差距。花一周把二进制互转练到条件反射之后的长期收益绝对远超这周的投入。