尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

二进制与十进制互转全解析:整数、小数、负数及精度处理

二进制与十进制互转全解析:整数、小数、负数及精度处理 1. 为什么二进制和十进制互转值得单独拿出来讲很多人第一次接触进制转换是在计算机基础课上。老师写一个除2取余的竖式再写一个按权展开的多项式然后说“记住就行”。结果到了实际用的时候比如看内存地址、分析协议报文、处理颜色值、做嵌入式寄存器配置脑子里的公式全乱套了。我自己就踩过这个坑早年调一个传感器数据手册写寄存器地址是0x3F我顺手转成十进制63结果在代码里写成了63H编译直接报错。进制转换这件事看起来简单但真正要做到“随手就能算、算完不出错”还是得把底层逻辑理顺。这篇内容就是围绕二进制与十进制互转展开的。我会把整数、小数、负数三种情况分开讲把“除2取余”和“按权展开”这两个核心方法拆到每一步都能复现再补充一些实际工作中高频出现的场景比如hex转十进制、0.6的二进制、217转化为二进制、负数的二进制这些热搜词背后的真实问题。不管你是刚学编程的新手还是工作几年偶尔被进制问题卡住的开发者都能从里面找到可以直接抄作业的步骤。需要提前说明的是进制转换本身不复杂但精度限制和符号处理是两个最容易翻车的地方。十进制小数转二进制时很多数转不干净比如0.6这时候要不要舍入、保留几位直接决定最终结果对不对。负数更是如此原码、反码、补码三套规则混在一起不搞清楚就会得出完全相反的数值。这些细节我会在对应章节里逐一拆解。2. 核心方法拆解从“除2取余”到“按权展开”2.1 十进制整数转二进制除2取余法的完整推演十进制整数转二进制最通用的方法就是除2取余逆序排列。这八个字很多人会背但实际操作时容易在“逆序”这一步出错。我拿热搜词里的217转化为二进制来完整走一遍。217除以2商108余1108除以2商54余054除以2商27余027除以2商13余113除以2商6余16除以2商3余03除以2商1余11除以2商0余1。把余数从下往上读11011001。所以217的二进制是11011001。验证一下1×128 1×64 0×32 1×16 1×8 0×4 0×2 1×1 128641681 217。对上了。这里有个实操技巧先列出2的幂次表从2^0到2^7分别是1、2、4、8、16、32、64、128。拿到一个十进制数先找小于等于它的最大2的幂然后做减法。比如217小于等于它的最大幂是128217-1288989小于等于它的最大幂是6489-642525小于等于它的最大幂是1625-1699小于等于它的最大幂是89-811对应2^0。所以二进制位在128、64、16、8、1这五个位置上是1其余是0得到11011001。这个方法比反复除2更快心算就能完成。注意除2取余法得到的余数序列必须从最后一个余数读到第一个余数。我见过太多人直接按计算顺序写结果得到1011011这种反向结果。建议每算一次余数就写在纸上最后从下往上抄一遍。2.2 二进制转十进制整数按权展开的三种计算路径二进制转十进制核心是按权展开求和。每一位上的数字乘以对应的2的幂次然后全部加起来。比如11011001从右往左权值依次是2^0到2^7计算过程就是上面验证的那一串加法。实际工作中我常用三种路径路径一直接按权展开。适合位数少的情况比如8位以内的二进制口算就能完成。路径二分组转换。把二进制从右往左每4位分一组每组对应一个十六进制数字先把二进制转成十六进制再把十六进制转成十进制。比如11011001分成1101和1001分别是D和9即0xD9然后13×169217。这个方法在hex转十进制场景里特别常用。路径三移位累加法。从左到右扫描二进制遇到1就“当前值×21”遇到0就“当前值×2”。比如110110011→11→30→61→131→270→540→1081→217。这个方法写代码时最方便不需要预先知道总位数。三种路径没有优劣之分关键是看场景。手工计算用路径一或路径二写程序用路径三。2.3 十进制小数转二进制乘2取整与精度陷阱十进制小数转二进制方法是乘2取整顺序排列。拿热搜词里的0.6的二进制来演示。0.6×21.2取整数部分1剩下0.20.2×20.4取整数部分0剩下0.40.4×20.8取整数部分0剩下0.80.8×21.6取整数部分1剩下0.60.6×21.2取整数部分1剩下0.2……到这里你会发现0.6又回来了开始循环。所以0.6的二进制是0.100110011001...循环节是1001。这就是精度限制的来源。十进制小数转二进制时只有那些能表示成“分母是2的幂次”的分数才能转干净比如0.5、0.25、0.125。0.6的分母是5转不干净必须舍入。热搜词里问“十进制小数转换为二进制有精度限制时需要考虑舍入吗”答案是必须考虑。IEEE 754单精度浮点数只保留23位尾数双精度保留52位超出部分会被舍入。如果你在做金融计算或者高精度传感器数据处理舍入误差累积起来可能让结果偏出可接受范围。实操建议涉及金额计算时不要用浮点数存二进制转换结果直接用整数分或者定点数。如果必须用浮点至少用双精度并且在比较时设置一个容差阈值比如1e-9。2.4 二进制小数转十进制负权展开与快速估算二进制小数转十进制权值从2^-1开始依次是0.5、0.25、0.125、0.0625……比如0.1001计算方式是1×0.5 0×0.25 0×0.125 1×0.0625 0.5625。快速估算时可以只取前几位。比如0.100110011...前四位是0.1001对应0.5625已经很接近0.6了。如果精度要求不高取前8位就够用。但要注意截断和舍入是两回事。截断直接丢掉后面的位舍入要看下一位是否大于等于1。比如0.10011截断到4位是0.1001舍入到4位是0.1010因为第5位是1需要进位。3. 负数、除法与扩展法那些热搜词背后的真实问题3.1 负数的二进制表示原码、反码、补码的递进关系负数的二进制是热搜里的高频问题。很多人知道负数在计算机里用补码表示但说不清楚为什么。我按递进关系捋一遍。假设用8位表示一个数。原码是最直观的最高位是符号位0表示正1表示负其余7位是数值。比如5是00000101-5是10000101。但原码有个问题0有两种表示00000000和10000000而且加减法需要单独处理符号位电路设计麻烦。反码的规则是正数不变负数在原码基础上符号位不变、其余位取反。-5的原码是10000101反码是11111010。反码解决了部分运算问题但0仍然有两种表示。补码的规则是正数不变负数在反码基础上加1。-5的反码是11111010加1得到11111011。补码的好处是0只有一种表示00000000而且减法可以统一成加法。验证一下5的补码是00000101-5的补码是11111011两者相加得到100000000超出8位被丢弃剩下00000000正好是0。注意从补码反推十进制时如果最高位是1说明是负数。先把补码减1得到反码再取反得到原码最后读出数值。比如11111011减1得11111010取反得10000101符号位是1表示负数值位是5所以是-5。3.2 二进制除法从长除法到移位减法二进制除法在热搜里出现说明很多人卡在二进制运算上。二进制除法其实和十进制长除法逻辑一样只是商只有0和1两种可能。举个例子计算1101除以10即13除以2。从被除数最高位开始取足够位数让除数能减先取11大于10商1余1把下一位0拉下来得到10等于除数商1余0把下一位1拉下来得到1小于除数商0余1。所以商是110即6余数是1。验证13÷26余1正确。在硬件层面除法通常用移位减法实现把除数左移对齐被除数最高位然后做减法能减就商1不能减就商0然后右移除数继续。这个过程和手工长除法完全一致。3.3 二进制扩展法位宽扩展时的符号处理二进制扩展法指的是把窄位宽的数扩展到宽位宽。比如8位的11111011-5扩展到16位应该得到1111111111111011而不是0000000011111011。前者是符号扩展后者是零扩展。规则很简单如果原数是有符号数最高位是1就补1最高位是0就补0如果原数是无符号数直接补0。这个规则在写汇编、做协议解析时经常用到。比如从网络收到的1字节有符号温度值扩展到4字节整数时必须做符号扩展否则负温度会变成很大的正数。3.4 会计十进制与二进制人工智能两个容易混淆的概念热搜词里有个会计十进制这其实指的是BCD码Binary-Coded Decimal用4位二进制表示1位十进制数字。比如数字29BCD码是00101001高4位表示2低4位表示9。会计系统用BCD码是为了避免浮点误差因为金额的每一位都精确对应十进制数字。二进制人工智能则是指用二进制神经网络做推理权重和激活值都量化成1和-1乘法变成同或运算大幅降低计算量。这和进制转换本身关系不大但底层还是二进制表示和运算。4. 实操场景与工具链从手算到代码实现4.1 手算速查表与心算技巧日常工作中我建议把下面这张表记熟十进制二进制十六进制000000100011200102300113401004501015601106701117810008910019101010A111011B121100C131101D141110E151111F这张表覆盖了4位二进制和1位十六进制的全部对应关系。记住之后任意二进制转十六进制就是每4位查一次表任意十六进制转二进制就是每位展开成4位。hex转十进制也可以两步走先转二进制再按权展开或者直接用十六进制按权展开权值是16的幂次。心算技巧方面我常用凑数法拿到十进制数先看它最接近哪个2的幂然后做差再对差值重复这个过程。比如217最接近256但超了所以从128开始217-1288989-642525-1699-811-10。对应位是128、64、16、8、1即11011001。4.2 代码实现Python、C与JavaScript的进制转换Python里最方便内置函数直接搞定# 十进制转二进制 bin(217) # 0b11011001 # 二进制转十进制 int(11011001, 2) # 217 # 十进制转十六进制 hex(217) # 0xd9 # 十六进制转十进制 int(d9, 16) # 217C语言没有内置的二进制输出需要自己写#include stdio.h void print_binary(unsigned int n) { if (n 1) print_binary(n 1); putchar(0 (n 1)); } int main() { print_binary(217); // 输出11011001 return 0; }JavaScript里可以用toString和parseInt(217).toString(2) // 11011001 parseInt(11011001, 2) // 217 (217).toString(16) // d9 parseInt(d9, 16) // 217注意C语言里处理负数时是算术右移还是逻辑右移取决于编译器实现。做进制转换时建议先用unsigned类型接收避免符号位干扰。4.3 小数转换的代码实现与舍入控制Python的float转二进制没有直接内置函数需要手动实现def float_to_binary(num, precision10): integer_part int(num) decimal_part num - integer_part binary_integer bin(integer_part)[2:] binary_decimal for _ in range(precision): decimal_part * 2 bit int(decimal_part) binary_decimal str(bit) decimal_part - bit return binary_integer . binary_decimal print(float_to_binary(0.6, 8)) # 输出0.10011001这里precision参数控制保留几位小数。如果要做舍入而不是截断可以在循环结束后判断下一位是否为1是则对最后一位加1并处理进位。4.4 实际案例解析一个传感器数据帧假设你收到一串十六进制数据D9 3F 00 00协议文档说第一个字节是无符号整数第二个字节是有符号整数后两个字节组成一个小端序的16位无符号整数。转换过程如下D9转十进制13×169217。3F转十进制3×161563。作为有符号数最高位是0所以是63。00 00小端序低字节在前值是0。如果第二个字节是FB转十进制是251最高位是1说明是负数。补码转原码251-1250取反得5加负号得-5。这个案例里进制转换只是第一步更重要的是结合协议定义判断符号和字节序。我见过有人把有符号数当无符号数处理结果温度从-5变成了251直接触发告警。5. 常见问题与排查技巧实录5.1 进制转换常见错误速查表问题现象可能原因排查方法二进制转十进制结果偏大把符号位当数值位计算检查最高位是否为1确认是否有符号十进制转二进制结果位数不对忘记补前导零确认目标位宽不足位补0小数转换结果循环分母不是2的幂次确认精度要求设置舍入规则十六进制转二进制少位每位十六进制必须展开成4位检查是否漏掉前导零负数补码反推错误忘记减1或取反顺序错按“减1→取反→读数值”顺序操作字节序导致数值错误大端小端混淆确认协议定义的字节序5.2 精度问题的实际处理经验做嵌入式开发时我遇到过ADC采样值转电压的问题。ADC是12位参考电压3.3V采样值2048对应1.65V。计算方式是2048/4096×3.3。如果用浮点数结果是1.6500000000000001看起来没问题。但如果把2048先转成二进制再处理中间步骤的舍入误差可能让结果偏出0.001V。我的做法是全程用整数运算2048×3300/4096先乘后除结果1650单位毫伏精确无误差。经验涉及物理量转换时尽量把单位放大到整数比如伏特转毫伏、安培转毫安避免浮点运算。如果必须用浮点用双精度并在最后一步做舍入。5.3 工具使用中的坑计算器模式与编程模式Windows计算器有“标准”和“程序员”两种模式。程序员模式支持二进制、八进制、十进制、十六进制互转但要注意位宽设置。默认是QWORD64位如果你输入一个8位数它可能显示成64位扩展后的结果。比如输入11111011在BYTE模式下显示11111011在QWORD模式下显示11111111111111111111111111111011。做8位有符号数转换时一定要把位宽切到BYTE。在线转换工具也有类似问题。有些工具默认把输入当无符号数负数转换会出错。我一般用Python的int()函数加base参数可控性最强。5.4 从二进制角度看strstr与内存查找热搜词里有个c语言strstr()能否用于查找二进制内存。答案是不能直接用于二进制数据。strstr()按字符串处理遇到\0就停止而二进制数据里\0是合法字节。如果非要用必须确保数据中没有\0或者改用memmem()函数。这个问题的本质是二进制数据没有“字符串结束符”的概念任何按字符串设计的函数都不能直接用于二进制。6. 进阶话题位运算与进制转换的配合使用6.1 位掩码与移位提取字段进制转换经常和位运算一起用。比如一个32位寄存器高16位是地址低16位是数据。提取地址的方法是(reg 16) 0xFFFF提取数据的方法是reg 0xFFFF。这里0xFFFF是掩码二进制是16个1。理解掩码的关键是与1做与运算保留原值与0做与运算清零。6.2 二进制表示十进制小数的精度分析回到0.6的二进制这个问题。0.6的二进制是无限循环小数循环节1001。如果用双精度浮点数存储实际存储的是0.59999999999999997779553950749686919152736663818359375。这个值和0.6的误差是2.2e-17在大多数场景下可以忽略。但如果做100万次累加误差会放大到2.2e-11仍然很小。真正的问题出现在比较运算0.1 0.2 0.3在浮点数里是false因为0.1和0.2的二进制表示都有舍入误差。处理方案比较浮点数时用abs(a - b) 1e-9或者用decimal模块做精确十进制运算。6.3 不同位宽下的数值范围速查位宽无符号范围有符号范围8位0 ~ 255-128 ~ 12716位0 ~ 65535-32768 ~ 3276732位0 ~ 4294967295-2147483648 ~ 214748364764位0 ~ 1.8e19-9.2e18 ~ 9.2e18这张表在做协议解析和类型转换时非常有用。比如收到一个16位有符号数范围是-32768到32767如果实际物理量范围是-40到125摄氏度需要做线性映射。7. 我个人的实操体会进制转换这件事我最大的体会是不要依赖心算但要把心算练到能快速验证。手边常备一张2的幂次表和十六进制对应表遇到转换先查表再用心算复核。写代码时优先用语言内置的转换函数但一定要测试边界值比如0、-1、最大正数、最小负数。小数转换永远要问一句精度够不够要不要舍入。负数转换永远要确认位宽是多少有没有符号扩展。最后分享一个我常用的检查方法双向验证。十进制转二进制之后再把二进制转回十进制看是否等于原数。如果不等说明中间某一步错了。这个方法虽然笨但能抓住99%的错误。
返回列表