【发布时间】:2021-07-01 10:46:01
【问题描述】:
假设必须将两个有符号数相加(或相减等),如下所示:
short short_sum (int i1, int i2) {
assert (SHRT_MIN <= i1 && i1 <= SHRT_MAX);
assert (SHRT_MIN <= i2 && i2 <= SHRT_MAX);
return (short)(i1 + i2);
}
我需要模拟相同的效果,但将 int 和 short 分别替换为有符号字节和(理论上)5 位有符号整数。类似于以下内容:
signed char tiny_sum (signed char c1, signed char c2) {
assert (-16 <= c1 && c1 <= 15);
assert (-16 <= c2 && c2 <= 15);
return (signed char)(((c1 + c2) << (CHAR_BIT - 5)) >> ((CHAR_BIT - 5)));
}
虽然上面的代码成功了,但它有一些缺陷:右移负数是实现定义的,更糟糕的是,左移这样的数字,或者左移“溢出”的正数,是不明确的。所以代码既不可移植,也不真正可预测。
所以这是我的问题:如何通过快速、便携和明确定义的位操作来达到预期的效果?
提前致谢。
【问题讨论】:
-
你考虑过位域吗?编译器会为你处理这个。
-
为什么不只是
return (c1 + c2) & 0x1f;?portable你的目标是便携吗,包括非二进制补码系统?做什么的?right-shifting a negative numbertwos-complement 具有负数只是正数的属性。只需转换为无符号并对无符号执行所有操作。尽管如此,您的代码仍然取决于机器是二进制补码。assert (-16 <= c1 && c1 <= 15);并不意味着该数字是二进制补码。值!= 位表示。除非您使用二进制补码系统并且您知道。 -
谢谢,@KamilCuk 脑子里有两种语言;混合语法。我的错。 :-)
-
嗨@StephenNewell。事实上,我已经考虑过位域。但是我需要在没有位域的 Eiffel 中而不是在 C 中进行此类操作。 Eiffel shift 等被转换为 C 中的等效操作,因此它们容易受到未定义行为的影响。
-
嗨@KamilCuk。 AFAIK,
return (c1 + c2) & 0x1f;会将每个结果都变成正值,不是吗?我的断言只是为了澄清预期范围。我不太可能偶然发现非二进制补码系统。但是编译器不在乎。它可能会“优化”我无法定义的代码。如果我能保留一个 5 位(这个宽度可能会改变)有符号整数(二进制补码)的语义,我会很满意。
标签: c bit-manipulation twos-complement