补码原码反码转换

计算机里存负数用的是补码。同一个位模式按补码、反码、原码读会得到不同的十进制数,混用是查位运算 bug 的常见起点。

补码与浮点的常见坑

补码让减法变成加法。补码最大的好处是不需要单独的减法电路:a - b 直接算成 a + (-b的补码),硬件上加法器就够了。而且零只有一种表示,不会出现正零和负零比较不相等的情况。反码和原码都有正负零,还要单独处理进位回卷,所以现代 CPU 一律用补码。

最小负数没有对应的正数。8 位补码范围是 -128 到 127,不是对称的 -127 到 127。原因是要给零留一种表示,于是负半边多出一个。这导致 -128 取相反数还是 -128(溢出回卷),取绝对值也是负数。写代码时要小心 abs(INT_MIN) 这类陷阱。

0.1 在二进制里存不精确。十进制小数转二进制要不断乘二取整,0.1 会得到无限循环的小数,单精度只能存最近似的值(约 0.100000001490116)。所以 0.1 + 0.2 不等于 0.3,做金额计算要用整数分或专门的十进制类型,不能直接用浮点。

浮点比较不能直接用等号。因为存在舍入误差,应该比较两个数之差的绝对值是否小于容差。另一个坑是大数吃小数:单精度只有约 7 位有效数字,1e8 加上 1 还是 1e8,做累加时如果量级相差太大会把小数项整个丢掉。

非规格化数换来了渐进下溢。当指数域全为零时,尾数不再有隐含的 1,数值可以一直小到 2⁻¹⁴⁹(单精度)。这让「非常小的数相减是否为零」这件事有了答案,而不是突然掉到零。代价是处理非规格化数在部分硬件上会明显变慢。