C 中的按位饱和加法 (HW)
我正在做一项作业,但我不知道如何实现它。我必须创建一个函数 sadd(int x, int y)
,它返回相加的数字,除非它溢出(然后只返回最大可能的 int)。我已经能够提出一些涉及强制转换和条件语句的解决方案,但解决方案中不允许这些。仅运算符 ~ ! ^ + << >>> &
和 |
。
I'm working on an assignment and I can't figure out how to implement this. I have to make a function sadd(int x, int y)
that returns the numbers added together unless it overflows (then just return the max possible int). I've been able to come up with some solutions involving casting and conditional statements, but those aren't allowed in the solution. Only the operators ~ ! ^ + << >> &
and |
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对于有符号数的加法,如果将两个相同符号的数字相加并得到不同符号的结果,就会发生溢出。由于涉及范围,两个不同符号的数相加时不可能产生溢出。
因此,您可以做的是 - 仅观察符号位(二进制补码中最重要的一位) - 使用异或来确定两个原始数字的符号是否不同,对其进行补码,以便在以下情况下得到“0”:它们是不同的,“1”表示相同。
然后,您可以对结果与输入之一使用异或。如果它们相同,则给出“0”;如果不同,则给出“1”。
如果两个输入相同但结果不同,则这两个结果加在一起得到总体“1”,否则为“0”。
然后,您可以使用移位和 OR 的组合来用该值填充整个整数。假设您使用的是 32 位整数,只需设置最低 31 位即可获得最高值的正整数。然后,您可以对任一输入的符号位进行类似的移位和或运算。异或结果。如果输入为负数,则会给出最低值整数。
编辑:哦,并使用是否存在溢出的位值,扩展以填充 int,通过将其与如果存在溢出则返回的结果进行相加来选择要返回的值,对其进行补足并与正常情况进行相加加法结果,然后将两者或(或相加)在一起。
Presto:所有二进制逻辑,没有条件。我认为,因为这是家庭作业,您不需要实际的代码?
九年后,我同意下面@gman的评论;为了仅使用允许的运算符来实现饱和加法,您必须依赖未定义的行为 - 并且上面的答案隐式地这样做了。
这样做的一个重大风险是编译器知道哪些行为是未定义的,并且可能在优化期间利用它。了解底层架构(例如,它是二进制补码,它进行有符号移位)不足以预测编译器的输出。
稳健的生产实现是可能的,但需要条件语句,因此不会回答这个问题。
For addition of signed numbers, overflow has happened if you add two numbers of the same sign and get a result with a different sign. Because of the ranges involved, it is impossible to generate overflow when adding two numbers of different signs.
So, what you can do is — watching the sign bit only (the most significant one in two's complement) — use exclusive OR to get to whether the two original numbers differed in sign, complement that so that you've got '0' if they were different, '1' for the same.
You can then use exclusive OR on the result versus one of the inputs. That'll give '0' if they were the same, '1' if they were different.
And those two results together to get an overall '1' if the two inputs were the same but the result was different, '0' otherwise.
You can then use a combination of shifts and ORs to fill an entire integer with that value. Supposing you're in a 32 bit integer, just set the lowest 31 bits to get the highest value positive integer. What you can then do is a similar sets of shifts and ORs on the sign bit of either of the inputs. Exclusive OR the results. That'll instead give the lowest value integer if the inputs were negative.
EDIT: oh, and use the bit value of whether there was overflow, extended out to fill the int, to select what value to return by anding it with the result you would return if there were overflow, complementing it and anding it with the normal additive result, then oring (or adding) the two together.
Presto: all binary logic, no conditionals. I assume, because it's homework, that you don't want actual code?
Nine years later, I agree with the comment of @gman below; in order to implement saturating addition using only the permitted operators, you've got to rely on undefined behaviour — and the answer above implicitly does so.
A substantial risk with that is that compilers know which behaviour is undefined and may exploit that during optimisation. Knowledge of the underlying architecture (e.g. that it is two's complement, that it does signed shifts) is not sufficient to predict a compiler's output.
A robust production implementation is possible, but would require conditional statements and therefore would not answer this question.
来自 ARM 网站 (ARM 信息中心)内在函数:
该示例在 ISO C 中不安全;有符号整数溢出是未定义的行为,因此您需要在检测它时避免引起它。
c = a + (unsigned)b;
可以避免该问题,给出预期的二进制整数结果,因为 2 的补码加法与无符号二进制相同,但在 Cunsigned
类型中有明确的包装。From the ARM Website (ARM Info Center) on intrinsic functions:
This example is unsafe in ISO C; signed integer overflow is undefined behaviour so you need to avoid causing it as part of detecting it.
c = a + (unsigned)b;
would avoid the problem, giving the expected binary integer result because 2's complement addition is the same as unsigned binary, but in Cunsigned
types have well-defined wrapping.