在计算机中,实现乘除运算的方案通常有以下3种:
-
软件实现。在低档微机中无乘除运算指令,只能用乘法和除法子程序来实现乘除运算。
-
在原有实现加减运算的运算器基础上增加一些逻辑线路,使乘除运算变换成加减和移位操作。在机器中设有乘除指令。
-
设置专用的乘、除法器,机器中设有相应的乘除指令。
不管采用什么方案实现乘除法,基本原理是相同的。如果采用第二种方案,则必然会涉及移位操作。
4.3.1带符号数的移位操作
一、带符号数移位操作概述
带符号数的移位分为算术移位和逻辑移位两种,主要区别在于符号位的处理方式:
- 算术移位:保持符号位不变,仅对数值部分移位
- 逻辑移位:所有位(包括符号位)都参与移位
二、算术移位规则(以8位为例)
1. 正数移位(符号位为0)
操作 | 示例(35的补码) | 结果 |
---|---|---|
左移 | 00100011 << 1 | 01000110 (70) |
右移 | 00100011 >> 1 | 00010001 (17) |
2. 负数移位(符号位为1)
操作 | 示例(-35的补码) | 结果 |
---|---|---|
左移 | 11011101 << 1 | 10111010 (-70) |
右移 | 11011101 >> 1 | 11101110 (-18) |
关键特点:
- 左移相当于×2(可能溢出)
- 右移相当于÷2(向下取整)
- 符号位始终不变
三、逻辑移位规则
四、不同编码的移位差异
编码类型 | 左移空位补 | 右移空位补 |
---|---|---|
原码 | 0 | 0 |
反码 | 1 | 1 |
补码 | 0 | 1 |
五、移位操作实现原理
六、注意事项
- 溢出问题:左移可能导致数值超出表示范围
- 补码表示范围:-2ⁿ⁻¹ ~ 2ⁿ⁻¹-1
- 循环移位:分为带进位位和不带进位位两种
- 实际应用:
- 快速乘除:左移1位=×2,右移1位≈÷2
- 数据提取:通过移位+掩码获取特定位
七、典型错误示例
错误:对负数执行逻辑右移后误认为结果仍是负数
# Python示例(>>是算术右移,>>>是逻辑右移)
x = -35
print(bin(x)) # -100011
print(bin(x >> 1)) # -10010 (算术)
print(bin(x >>> 1)) # 1101110 (逻辑,Python需用& 0x7F处理)
4.3.2带符号数的舍入操作
带符号数的舍入操作主要用于处理运算后超出存储位数的情况。
一、舍入方式分类
-
截断法(Truncation)
- 直接丢弃多余的低位。
- 简单但误差较大,适用于快速计算。
-
四舍五入(Round Half Up)
- 若截断部分 ≥ 0.5LSB(最低有效位),则进1。
- 在二进制中表现为:
- 若截断部分的最高位为1且后续位不全为0,则进位。
-
向偶数舍入(Round to Nearest, Even)
- 当截断部分为0.5LSB时,选择结果为偶数。
- 减少统计偏差,被IEEE标准采用。
-
向上取整(Round Up)
- 正数向上进1,负数向零舍入。
-
向下取整(Round Down)
- 正数直接截断,负数进1。
二、补码与原码的舍入差异
数值类型 | 示例(保留3位小数) | 截断法 | 四舍五入 |
---|---|---|---|
正数补码 | 0011.1010 (3.625) | 0011 (3) | 0100 (4) |
负数补码 | 1100.1101 (-3.8125) | 1100 (-4) | 1100 (-4) |
三、四舍五入操作流程图
四、IEEE标准向偶数舍入示例
假设保留3位二进制小数:
原值(二进制) | 截断部分 | 操作 | 结果 |
---|---|---|---|
1.1101 | 0.0001 | 直接截断 | 1.110 |
1.1110 | 0.0100 | 截断部分=0.25 | 1.111 |
1.1101_1000 | 0.1000 | 中间值且低位为0 | 1.110 (偶数) |
1.1011_1000 | 0.1000 | 中间值但前位为1 | 1.101 → 0位变0 → 1.110 (变为偶数) |
五、不同舍入方式的误差分析
方法 | 最大误差(LSB) | 应用场景 |
---|---|---|
截断法 | 1.0 | 高速计算(如GPU) |
四舍五入 | 0.5 | 通用计算 |
向偶数舍入 | 0.5 | IEEE浮点标准(统计精度更高) |
向上/向下取整 | 1.0 | 金融累计保底逻辑 |