位运算的运用饱和加法函数实现

题目出自:《深入理解计算机系统》

【题目内容】

写出具有如下原型的函数的代码:

/* Addition that saturates to TMin or TMax•/

int saturating_add(int x, int y);

同正常的补码加法溢出的方式不同,当正溢出时,饱和加法返回 TMax,负溢出时,返回 TMin。饱和运算常常用在执行数字信号处理的程序中。

在程序中,你可以使用整型常数 INT_MAX 和 INT_MIN 来代表 TMax 和 TMin。

程序的其他部分已经为你准备好。函数应该遵循位级整数编码规则。

位级整数编码规则:

假设:

- 整数用补码形式表示;

- 有符号数的右移是算术右移;

- 数据类型 int 是 w 位长的。对于某些题目,会给定 w 的值,但是在其他情况下,只要 w 是 8 的整数倍,你的代码就应该能工作。你可以用表达式 sizeof(int) << 3 来计算 w。

禁止使用:

- 条件语句(if 或者 ?:)、循环、分支语句、函数调用和宏调用;

- 除法、模运算和乘法;

- 相对比较运算(>、<、≤ 和 ≥)。

允许的运算:

- 所有的位级和逻辑运算;

- 左移和右移,但是位移量只能在 0 到 w−1 之间;

- 加法和减法;

- 相等(==)和不等(!=)测试。(在有些题目中,也不允许这些运算);

- 整型常数 INT_MAX 和 INT_MIN;

- 对 int 和 unsigned 进行强制类型转换,无论是显式的还是隐式的。

 

题目分析:

 我们要实现饱和加法,要知道饱和的情况。我们知道,当两个很大的int相加时,结果可能会超出int范围,变成一个负数,同理两个很小的负数相加也可能会变成一个正数,int的范围是-2147483648~2147483647(即-2^31 ~ 2^31 - 1),int是一个循环,超出了INT_MAX就从INT_MIN继续往上数。

正溢出的情况是:正 + 正 = 负,负溢出:负 + 负 = 正

正数符号位是0, 负数符号位是 1。所以可以先求出x, y , x + y 的符号位,来判断释放有溢出,但是又不能使用分支语句,只能想办法用位运算来实现。所以可以要想办法构造两个掩码,一个标记正溢出,一个标记负溢出,掩码是全1或者全0,这样与INT_MAX,INT_MIN  按位与之后就可以要么不变,要么变为0,再加上未溢出的情况,就有三个结果,它们要么是不变要么是0,把这三个数按位或上就可以得到最终答案。

return (mask_pos & INT_MAX) | (mask_neg & INT_MIN) | ((~mask_pos & ~mask_neg) & sum);

掩码全1就是有溢出,全0就是未溢出。

符号位可以通过左移31位得到。(x_sign, y_sign, sum_sign)

溢出标志(正溢出为例):(x_sign == 0) && (y_sign == 0) && (sum_sign == -1)

进一步变形就是:pos_overflow = (x_sign | y_sign == 0) & (sum_sign == -1); 

得到的结果要么是1,要么是0。取负就可以得到全1或者全0。至此,就可以做出来了

#include <stdio.h>
#include <limits.h>

int saturating_add(int x, int y) {
	int sum = x + y;
	int w = sizeof(int) << 3;//求int的位数 
	int x_sign = x >> (w - 1);// 得到x的符号位
	int y_sign = y >> (w - 1);// 得到y的符号位
	int sum_sign = sum >> (w - 1);// sum的符号位
	
	//判断是正溢出还是负溢出 
	int pos_overflow = (x_sign | y_sign == 0) & (sum_sign == -1);
	int neg_overflow = (x_sign & y_sign == -1) & (sum_sign == 0);
	
	//得到掩码 
	int mask_pos = -pos_overflow;
	int mask_neg = -neg_overflow;
	
	return (mask_pos & INT_MAX) | (mask_neg & INT_MIN) | (~(mask_pos & mask_neg) & sum);
}

int main() {
    int x, y;
    scanf("%d %d", &x, &y);
    printf("%d\n", saturating_add(x, y));
    return 0;
}

### 饱和运算的概念与实现方式 饱和运算是指在数值计算过程中,当结果超出指定范围时,将结果限制为该范围内的最大值或最小值。这种运算通常用于避免数据溢出问题,尤其是在图像处理、数字信号处理等领域中非常常见[^1]。 在图像处理中,饱和运算的典型应用是图像增强、HDR合成等场景。例如,在使用OpenCV进行图像加法操作时,如果像素值超过255(对于8位灰度图像),饱和运算会将其截断为255,从而保持高光区域的亮度[^2]。相比之下,模运算则会导致像素值“绕回”到较低值,可能引起视觉上的噪声或失真。 #### 饱和运算的实现方式 饱和运算可以通过多种方法实现,具体取决于硬件或软件环境: 1. **基于硬件的实现** 在FPGA或DSP等硬件环境中,饱和运算通常通过逻辑电路实现。例如,在定点数运算中,当整数部分发生溢出时,可以选择Wrap(循环)或Saturate(饱和)模式。Saturate模式会将所有超出范围的值固定为最大值或最小值[^3]。 2. **基于软件的实现** 在软件层面,饱和运算可以通过条件判断或位运算实现。以下是一个简单的C语言实现示例,用于对单字节(8位)数据进行饱和处理: ```c unsigned char saturate(unsigned char value) { if (value > 255) return 255; if (value < 0) return 0; return value; } ``` 另一种更高效的位运算实现方式如下[^4]: ```c Pointer[X] = (byte)((((ushort)Value | ((short)(255 - Value) >> 15)) & ~Value >> 15); ``` 这段代码利用了位操作符(如`|`、`&`、`>>`等)来快速判断并限制值的范围。 3. **基于库函数实现** 在图像处理领域,OpenCV提供了内置的饱和运算支持。例如,`cv2.add`函数会对图像像素值进行加法运算,并自动执行饱和截断操作,确保结果不会超出[0, 255]范围[^2]。 #### 饱和运算的应用场景 - **图像处理**:在图像增强、HDR合成等场景中,饱和运算可以有效避免高光区域变暗的问题。 - **音频处理**:防止音频信号在放大过程中出现削波失真。 - **嵌入式系统**:在资源受限的环境中,饱和运算可以简化溢出处理逻辑,提高系统稳定性。 ### 注意事项 - 在选择饱和运算或模运算时,需根据具体应用场景决定。例如,图像增强通常需要饱和运算以保持视觉效果自然,而某些特殊效果可能更适合使用模运算[^1]。 - 对于多通道图像(如RGB图像),需要对每个通道分别进行饱和运算[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值