二进制计算器

对二进制数直接做加、减、乘、除和位运算,每一步都给出十进制读数与八位、三十二位补码视图。底层走 BigInt,超过 53 位也不会丢精度。

运算口径:P = A 运算 B。A、B 可以写成二进制(0b1100)、十进制(12)或十六进制(0xC);「取反」是一元运算,只用 A。取反结果 ~A 是 -A-1,本页同时给出它的补码位串。

结果(二进制)—
结果(十进制)—
8 位补码—
32 位补码—
运算式—
结果位宽—

提示:在任一输入框里按 Enter 立即重算;「取反」只用操作数 A,B 填什么都不参与。

使用方法

  1. 在「操作数 A」填第一个数。示例填 0b1100,即十进制 12;也可以直接填 12 或 0xC,三种写法本页都认。
  2. 在「运算」下拉里选一种。示例选「加法 +」。
  3. 在「操作数 B」填第二个数。示例填 0b1010,即十进制 10。选「按位取反 NOT」时这一栏被忽略。
  4. 只有选「左移」或「右移」时才需要动「移位位数 k」,允许 0 到 256;其余运算下 k 这一栏不参与。
  5. 点「计算」或按 Enter,二进制计算器的结果区同时给出二进制、十进制、8 位补码和 32 位补码四栏。

这个二进制计算器能解决什么问题

掩码、寄存器、校验和、位图这些活儿都在二进制上做,手算竖式太慢也容易错。本页由二进制计算器处理四类具体问题:一是位掩码合并,12 | 10 得到 0b1110(14),用一条指令把两个标志位置起来;二是判断某一位是否被置起,12 & 10 得到 0b1000(8),说明只有第 3 位同时为 1;三是取反求掩码,~12 不是 3 也不是 12,而是十进制 -13,它的 8 位补码是 11110011;四是移位做除法近似,12 >> 1 得 0b110(6),等价于整除 2。

本页只做「两个数之间的运算」。只把二进制换成十进制、或反过来,不带任何运算的,请用文末的进制转换页;本页刻意不做那件事,避免和它们抢同一个搜索意图。

原理和变量:用 BigInt 而不是 Number

本页所有运算都把输入交给 BigInt() 解析,然后直接用 + - * / % & | ^ ~ << >> 这些运算符。关键在于为什么不用 Number 加 parseInt。

JavaScript 的 Number 是 IEEE 754 双精度浮点,尾数只有 53 位有效位,Number.MAX_SAFE_INTEGER 恰好是 253-1 = 9007199254740991。更糟的是位运算:ECMA-262 规定位运算符会先把左操作数走一遍 ToInt32,也就是先截断成 32 位有符号整数再运算。实测 1e10 | 0 的结果是 1410065408,而正确的 BigInt(1e10) >> 1n 是 5000000000——用 Number 算,答案从 50 亿变成 14 亿。掩码运算里出现这种错误不会报错,只会悄悄给你一个错的位模式。

BigInt 是任意精度整数,BigInt('0b1100') 直接得到 12n,位运算不做 32 位截断,因此不存在上面那个坑。ES2020 把它纳入标准,typeof 12n === 'bigint',可以和 Number 混算但不能混做位运算。

解析时为什么必须处理前缀。BigInt(str) 会按字面量前缀判断进制:带 0b 是二进制,带 0x 是十六进制,0o 是八进制,什么都不带就是十进制。所以 BigInt('1010') 得到 1010n(一千零一十),而 BigInt('0b1010') 才得到 10n。这是本页在 readNum() 里显式判断前缀、再决定是否补 0b 的原因。

补码视图怎么算。把结果 R 与上掩码 2w-1 做位与,R & (2w-1),得到的就是该宽度下的位串,左侧补零到 w 位。这一步对负数同样成立:-1 & 255 得 11111111,正是 -1 的 8 位补码。补码遵循 IEEE 754 之外的通行约定——最高位为 1 时表示负数,取反加一即得绝对值。

两个取不到值的运算符。其一,BigInt 没有无符号右移:写 1n >>> 1n 会抛 TypeError: BigInts have no unsigned right shift, use >> instead,这是规范里的显式禁止,不是本页的限制。其二,除数为 0 会抛 RangeError: Division by zero,本页提前拦下并给中文文案。

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

打开页面时 A 填 0b1100(12)、B 填 0b1010(10)、运算选加法,二进制计算器给出的六栏是:

只改「运算」,A、B 不动,得到的是这一组数在二进制下的全部关系:

换成 A 填 0b11111111(255)、B 填 0b1(1)做加法,结果是 0b100000000(256),但 8 位补码栏显示 00000000——这就是 8 位溢出,看补码栏能立刻发现。

输入范围与对照表

三种前缀的对照,同一个十进制 12 的不同写法:

写法字面量解析结果说明
二进制0b110012必须带 0b,否则按十进制
十进制1212无前缀即十进制
十六进制0xC12掩码配置里最常用的写法
八进制0o1412文件权限位常用

4 位位模式全表(前 12 行默认显示,点「加载更多位模式」展开余下 4 行),这一段是掩码配置和十六进制对照片面的基础:

十进制4 位二进制0b 字面量十六进制8 位补码
000000b00000x000000000
100010b00010x100000001
200100b00100x200000010
300110b00110x300000011
401000b01000x400000100
501010b01010x500000101
601100b01100x600000110
701110b01110x700000111
810000b10000x800001000
910010b10010x900001001
1010100b10100xA00001010
1110110b10110xB00001011
1211000b11000xC00001100
1311010b11010xD00001101
1411100b11100xE00001110
1511110b11110xF00001111

常用位宽的上限值,掩码写 0xFF 还是 0xFFFF 取决于这里:

位宽最大值(十进制)全 1 的十六进制说明
4 位150xF一个半字节,十六进制一位
8 位2550xFF一个字节
16 位655350xFFFF字符编码的常用宽度
31 位21474836470x7FFFFFFF231-1,有符号 32 位的正数天花板
32 位42949672950xFFFFFFFF232-1,IPv4 地址上限
64 位184467440737095516150xFFFFFFFFFFFFFFFF264-1,超出 Number 安全整数

边界与精度

操作数上限 40 个字符。输入框的 maxlength 是 40,按最坏情况(二进制 40 位)结果约 1012;即使做乘法也只有 80 位,BigInt 轻松处理。移位位数单独限制在 0 到 256,因为 1n << 100000n 会生成 100001 位的数字串,直接塞进页面会把排版撑坏。

精度结论。本页不存在精度丢失问题。253+1 = 9007199254740993 用 Number 算会等于 253(253+1 === 253 为 true),用 BigInt 算就是精确的 9007199254740993。这就是本页坚持 BigInt 的唯一理由。

每条非法路径都有对应文案。空输入报「操作数 A 还没有内容」;含 2 到 9 的二进制串报出第一个非法字符的位置;带 e 或小数点报不支持科学计数法;纯除法时 B 为 0 报「除数不能为 0」;0b 后面没有数字报格式错误。全部指明字段和允许范围,不做静默归零。

整除的语义要记住。/ 在 BigInt 上是向零取整而不是向下取整,所以 -7n / 2n 得 -3n(不是 -4)。想要数学上的向下取整,得自己用 q - (a % b !== 0n ? 1n : 0n) 修正,本页的「整除」栏给的是向零结果,同时单列「取余 %」让你自己核对。

常见问题

为什么不能直接用 Number 做位运算?

因为 ECMAScript 规定位运算符先把操作数走 ToInt32,也就是截断成 32 位有符号整数。实测 1e10 | 0 得 1410065408,而 BigInt(1e10) >> 1n 得 5000000000。这种错误不抛异常,只是悄悄返回错误的位模式,掩码和校验和场景下极难察觉。

取反 ~12 为什么是 -13 而不是 3?

按位取反是对补码逐位翻转,结果按有符号数解释。12 的二进制是 1100,逐位翻转得 0011,但 BigInt 认为它是无限位补码下的 ...11111011,即十进制 -13。通式是 ~A = -A - 1,所以 ~12 = -13。要看它的位串请看 8 位补码栏 11110011。

为什么 1n >>> 1n 会报 TypeError?

规范明确禁止对 BigInt 使用无符号右移,因为 BigInt 没有固定位宽,「无符号」这个概念无从定义。想做纯逻辑右移,先转成 32 位视图:Number(x & 0xFFFFFFFFn) >>> n。直接写 >>> 属于规范层面的错误,不是本页的限制。

8 位补码显示 00000000,是不是算错了?

不是,这是 8 位溢出。255 + 1 = 256 的真实值是 0b100000000(9 位),塞进 8 位窗口时最高位被丢掉,剩下全 0。判断溢出要看「结果位宽」那一栏:它显示 9 就说明 8 位窗口装不下。

12 ÷ 10 的结果是 1,为什么不给 1.1?

因为 BigInt 只有整数,/ 必然整除,向零取整得 1n,余数在「取余 %」里给出。想得到小数,用十进制输入 12.0 也不行——BigInt('12.0') 会直接抛 SyntaxError。本页的定位是整数与位运算,不做二进制浮点。

输入 1010 为什么变成了 1010 而不是 10?

因为没有 0b 前缀。BigInt() 按字面量规则判断进制:无前缀是十进制,0b 是二进制,0x 是十六进制,0o 是八进制。1010 因此被读成一千零一十。想表示十号请写 0b1010,这也是本页对超过 9 的纯数字串会提示「疑似漏写 0b 前缀」的原因。

264-1 用 JavaScript 打印为什么变成 18446744073709552000?

那是因为先被 Number 接住了,尾数 53 位不够放下 20 位十进制数,末尾被四舍五入。本页内部全程用 BigInt,对照表里的 18446744073709551615 是精确值;你若在控制台用普通数字复算,看到的截断值不代表本页算错。

输入的操作数会被保存或上传吗?

不会。三个输入框的值只交给本页 IIFE 里的 BigInt() 解析,全页没有 fetch、没有 XMLHttpRequest,可在开发者工具 Network 面板自查。刷新后即恢复默认的 0b1100 与 0b1010。

相关工具