十进制转二进制转换器

整数除 2 取余、小数乘 2 取整,逐步列出每一步的商与余数;负数给出 8/16/32/64 位补码,同时附带八进制、十六进制与双精度位模式。

补码(16 位):不适用,1234.5678 含小数,补码只定义在整数上(小数的位模式见双精度卡片)。

十六进制4d2.915b
八进制2322.442555
双精度位模式0x40934A456D5CFAAD
小数部分16 位内未终止

计算过程(共 27 行,先显示 12 行)

阶段运算位
整数1234 ÷ 2 = 617 余 00
整数617 ÷ 2 = 308 余 11
整数308 ÷ 2 = 154 余 00
整数154 ÷ 2 = 77 余 00
整数77 ÷ 2 = 38 余 11
整数38 ÷ 2 = 19 余 00
整数19 ÷ 2 = 9 余 11
整数9 ÷ 2 = 4 余 11
整数4 ÷ 2 = 2 余 00
整数2 ÷ 2 = 1 余 00
整数1 ÷ 2 = 0 余 11
小数0.5678 × 2 = 1.1356,取 11

提示:数值框里按 Enter 就转换;整数部分的余数是从低位算到高位的,读的时候要从下往上拼。步骤超过 12 行时会先显示 12 行,点「加载更多」展开全部;「导出 CSV」带 UTF-8 BOM,Excel 打开不乱码。

使用方法

  1. 在「十进制数值」里填 1234.5678(示例值),可以带小数点与负号;数值框里直接按 Enter 就出结果,不用点按钮。
  2. 「小数位数」默认 16,表示小数部分最多展开 16 位,可填 1~64 之间的整数。想看清循环节就把位数调大,只关心整数位就调到 1。
  3. 「补码位宽」下拉框决定补码那一行按几位算,可选 8 / 16 / 32 / 64 位。只有整数才给补码,小数会显示「不适用」。
  4. 「每 4 位加空格」默认勾选,把位串按 4 位一组分开——4 位二进制正好对应 1 位十六进制,对照着看不数格子。
  5. 下面的「计算过程」表把每一步都列出来:整数部分是「÷ 2 取余」,小数部分是「× 2 取整」。示例一共 27 行,先显示 12 行,点「加载更多」展开其余 15 行;「导出 CSV」「复制步骤表」可以把整张表带走。

这个十进制转二进制转换器能解决什么问题

一是对齐位序与掩码。拿到一个十进制数要写进寄存器、拼进协议报文、或者核对某一位开关时,先看它占几位、哪些位是 1。示例里的 1234 转成 10011010010,一共 11 位;按 0 起编号,第 1 位是 1、第 4 位是 1、第 10 位是 1,其余为 0。这类判断用位串比用十进制快得多。

二是把浮点误差讲清楚而不是背下来。「0.1 加 0.2 不等于 0.3」是常识,但为什么不等、到底差在哪一位,把 0.1 按乘 2 取整展开一遍就明白了:它的循环节是 0011,长度 4 位,永远除不尽,双精度只能截到 53 位有效位为止。

三是当教具用。课本上写「除 2 取余、逆序排列」,但学生常常搞不清「逆序」是逆哪一段。步骤表把每一步的商、余数与得到的位并排列出,末位在哪一目了然:最后一行的余数就是最高位。

四是看负数的补码长什么样。-10 在 8 位下是 1111 0110,在 16 位下多出 8 个 1;写硬件寄存器、解析二进制协议里的有符号字段时,先知道该填哪一串,比事后调试省事。

本页不做这些事:不做字符编码(把字符 A 变成 01000001 属于编码转换,请用「文本转二进制」)、不做二进制的加减乘除(用「二进制计算器」)、不把位串反解回十进制小数(有专门的二进制转十进制页)、也不做任意精度整数——超过双精度能表示的范围会直接报错,而不是给你一串看着对、实际错的 0 和 1。

转换原理:除 2 取余、乘 2 取整与补码

这个十进制转二进制转换器把整数与小数分开处理。整数部分用「除 2 取余、逆序拼接」。代码里就是两行:r = q % 2 取余数、q = Math.floor(q / 2) 取商,一直做到商为 0。第一次得到的余数是最低位,所以要把每轮的余数往前插(bits = r + bits),这也是「逆序」两个字的来历。示例里 1234 要除 11 次,得到 11 位:10011010010。检验一下:1024 + 128 + 64 + 16 + 2 = 1234。

小数部分用「乘 2 取整」。取小数部分 f,算 f × 2,整数位就是下一位二进制数字,剩下的小数部分继续乘 2。示例里 0.5678 的第一轮是 0.5678 × 2 = 1.1356,取走整数位 1,剩下 0.1356 接着算;算满 16 位得到 1001000101011011,而且还没算完。这套算法之所以可靠,是因为「乘 2」在二进制浮点里只是把指数加 1,不引入新的舍入误差——所以前 53 位的结果是被 IEEE 754 精度允许的。

什么时候能除尽?判据不在小数本身,而在化简后的分母:一个分数在二进制下能有限展开,当且仅当它的最简分母是 2 的幂。0.5 = 1/2、0.25 = 1/4、0.0625 = 1/16 都能除尽;0.5678 = 2839/5000,5000 = 23 × 54,除掉 2 的因子后还剩 625,不是 2 的幂,所以永远除不尽。更进一步,循环节的长度等于 2 对 625 的乘法阶:2 对 5、25、125、625 的阶分别是 4、20、100、500,所以 0.5678 的二进制小数循环节长达 500 位;0.1 的分母化简后是 5,阶为 4,循环节就是 0011 这 4 位。这解释了为什么把「小数位数」调到 64 位也看不到重复——你需要的可能是 500 位。

计算与显示分开。IEEE 754 的双精度存储本身有误差,直接打印中间量会得到 0.6704000000000629 这种噪声。工具的做法是:位串的判定用完整的双精度值,一步都不截断;表格里显示的小数按 10 位有效数字,所以看到的是 0.5678 × 2 = 1.1356 这样干净的式子。整数商不参与这个舍入(它们是精确整数,直接原样输出),否则 9007199254740991 会被显示成 9.007199255e+15。

负数的补码按位宽取模。w 位补码的严格定义是「对 2w 取模后的那个非负同余代表」,代码里用 ECMAScript 的 BigInt.asUintN(w, BigInt(x)) 一步算出,示例:-10 在 8 位下得 246,位串 1111 0110;-1 在任意位宽下都是全 1;-128 在 8 位下是 1000 0000——它的相反数 +128 在 8 位补码里没有表示,所以 8 位能表示的范围是 -128~127,而不是对称的 ±127。常见的「按位取反再加一」只是这个定义的等价算法,遇到最小值时会溢出,取模的写法不会。

双精度位模式是另一套规则。小数的位模式遵循 IEEE 754 的 binary64 布局:1 位符号 + 11 位指数(偏移量 1023)+ 52 位尾数,尾数隐含一个前导 1,所以实际有效位是 53 位。示例里 1234.5678 的位模式是 0x40934A456D5CFAAD,指数域 10000001001 等于 1033,减去 1023 得真实指数 10(因为 1234.5678 约等于 1.2056 × 210)。补码与这个布局是两套互不兼容的表示:整数用补码,小数用符号加绝对值,所以本页对含小数的输入不会给补码。

完整示例(数值仅作演示)

示例条件与页面打开时的默认值完全一致:数值 1234.5678、小数位数 16、补码位宽 16 位、勾选每 4 位加空格。实测结果是:

顺带做一个对照:JavaScript 内置的 (1234.5678).toString(2) 给出的结果是 10011010010.100100010101101101010111001111101010101101,整数位 11 位、小数位 42 位,加起来刚好 53 位有效位——这不是巧合,而是双精度的有效位数上限。内置方法给的是「最短的、能原样读回来的」那一串,位数随数值浮动;本工具给的是「按你指定的小数位数逐位展开」的那一串,并且把每一步都摆出来,两者的用途不同。

输入范围与对照表

先看整数,从 0 到 16 的对照关系如下表。注意 0 的二进制是单独的一个 0,不是空串——很多手写实现会在这里返回空字符串。

十进制二进制四位分组八进制十六进制位数
000001
111111
21010222
31111332
4100100443
7111111773
8100010001084
101010101012A4
121100110014C4
151111111117F4
16100001 000020105
2551111 11111111 1111377FF8
123410011010010100 1101 001023224D211
900719925474099153 个 11111 1111 …—1F FFFF FFFF FFFF53

再看小数。能否除尽只取决于化简后的分母是不是 2 的幂,下面这张表把常见小数分成两拨:

十进制小数化简后的分数二进制(最多 16 位)是否终止
0.51/20.1终止(1 位)
0.251/40.01终止(2 位)
0.1251/80.001终止(3 位)
0.06251/160.0001终止(4 位)
0.11/100.0001100110011001不终止,循环节 0011(4 位)
0.21/50.0011001100110011不终止,循环节 0011(4 位)
0.33/100.0100110011001100不终止,循环节 0011(4 位)
0.051/200.0000110011001100不终止,循环节 0011(4 位)
0.56782839/50000.1001000101011011不终止,循环节 500 位

补码的可表示范围随位宽变化,这里列出实际会用到档位。注意范围是不对称的:负端比正端多一个数。

位宽可表示范围取值个数-10 的位串-1 的位串
8 位-128 ~ 1272561111 01101111 1111
16 位-32768 ~ 32767655361111 1111 1111 011016 个 1
32 位-2147483648 ~ 2147483647429496729628 个 1 加 011032 个 1
64 位-9223372036854775808 ~ 92233720368547758071844674407370955161660 个 1 加 011064 个 1

输入侧的限制有三条要说清:数值绝对值不能超过 9007199254740991(253−1),否则双精度已经不能精确表示整数;小数位数只能是 1~64 的整数,超范围会被拒绝;数值框只接受十进制写法,不接受 0x 前缀、千分位逗号和空格,填了会直接报错而不是被悄悄解析成别的数。

结果边界与舍入

常见问题

为什么 0.1 转成二进制永远转不完?

因为 0.1 = 1/10,化简后分母 10 = 2 × 5,除掉一个 2 还剩因子 5,不是 2 的幂。二进制小数只能在分母为 2 的幂时有限展开(0.5、0.25、0.125 都是这样)。0.1 的循环节是 0011,长度 4 位,双精度最多保留 53 位有效位,所以只能截断——这就是 0.1 + 0.2 = 0.30000000000000004 的根源。顺带一提,把小数位数调到 64 位时工具会显示「精确终止于第 55 位」,那是双精度近似值的展开长度(0.1 存成 3602879701896397 / 255),与「0.1 本身不终止」并不矛盾,细节见上一节的边界说明。

0.5678 展开 16 位还没完,要多少位才循环?

循环节长度是 500 位。0.5678 = 2839/5000,5000 = 23 × 54,奇因子是 625;循环节长度等于 2 对 625 的乘法阶,而 2 对 5、25、125、625 的阶分别是 4、20、100、500。工具的小数位数上限是 64 位,所以你看不到重复——这不是工具算错,而是 64 位本来就不够。

负数为什么显示成一串 1 开头的位串?

那是补码。w 位补码的定义是「对 2w 取模后的非负同余代表」,所以 -10 在 8 位下等于 256 − 10 = 246,位串 1111 0110。位宽越大,前面补的 1 越多:16 位是 1111 1111 1111 0110,32 位是 28 个 1 加 0110。工具里用 BigInt.asUintN 一步算出,避免手写「取反加一」在最小值处溢出。

JavaScript 里 (-10).toString(2) 得到 -1010,和这里不一样?

它们本来就不是一件事。toString(2) 输出的是「负号 + 绝对值」,不涉及位宽,也不涉及补码;而补码必须指定位宽才有意义(8 位和 16 位的 -10 位串长度不同)。要位模式就用补码,要数学意义上的二进制表示就用 toString(2),两者不能互相替代。

为什么小数没有补码?

补码是整数的一种编码规则,建立在对 2w 取模之上,小数没有「按位取模」这回事。IEEE 754 给小数用的是另一套:1 位符号位 + 11 位指数 + 52 位尾数,符号用「符号加绝对值」的方式单独记录。所以本页对含小数的输入不给补码,而是给出双精度位模式,两者对应的场景完全不同。

表格里的小数为什么比我输入的数字短了一点?

显示时按 10 位有效数字整理过,目的是不把 0.6704000000000629 这种浮点噪声摆在表格里。位串的判定没有被这一步影响,用的是完整的双精度值;整数商也不做这个处理,它们是精确值,改了反而会出错。

填一个特别大的整数会怎样?

绝对值超过 253−1 = 9007199254740991 会直接报错。原因是双精度能精确表示的整数到此为止:9007199254740993 存进去会变成 9007199254740992,差的 1 不会报任何错,却会让整条位串从某个位置起全部作废。如果你确实要处理更大的整数,应该用支持任意精度的 BigInt 自己算,本页不做这种静默降级。

每 4 位空一格有什么用?能直接当字符编码看吗?

空格是为了对齐:4 位二进制正好等于 1 位十六进制,分组之后 100 1101 0010 可以直接读成 4D2,和十六进制编辑器里的显示对得上。但要提醒一句:这里的 10011010010 是数值 1234 的二进制形式,不是某个字符的编码;想把文字变成二进制要用「文本转二进制」,那才是按 UTF-8 逐字节展开。

相关工具