【问题标题】:How to simulate two's complement arithmetics upon 5-bit representation?如何在 5 位表示上模拟二进制补码算法?
【发布时间】: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) &amp; 0x1f;portable 你的目标是便携吗,包括非二进制补码系统?做什么的? right-shifting a negative number twos-complement 具有负数只是正数的属性。只需转换为无符号并对无符号执行所有操作。尽管如此,您的代码仍然取决于机器是二进制补码。 assert (-16 &lt;= c1 &amp;&amp; c1 &lt;= 15); 并不意味着该数字是二进制补码。值!= 位表示。除非您使用二进制补码系统并且您知道。
  • 谢谢,@KamilCuk 脑子里有两种语言;混合语法。我的错。 :-)
  • 嗨@StephenNewell。事实上,我已经考虑过位域。但是我需要在没有位域的 Eiffel 中而不是在 C 中进行此类操作。 Eiffel shift 等被转换为 C 中的等效操作,因此它们容易受到未定义行为的影响。
  • 嗨@KamilCuk。 AFAIK,return (c1 + c2) &amp; 0x1f; 会将每个结果都变成正值,不是吗?我的断言只是为了澄清预期范围。我不太可能偶然发现非二进制补码系统。但是编译器不在乎。它可能会“优化”我无法定义的代码。如果我能保留一个 5 位(这个宽度可能会改变)有符号整数(二进制补码)的语义,我会很满意。

标签: c bit-manipulation twos-complement


【解决方案1】:

使用https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend 做:

#include <stdio.h>

// start with good typedefs...
typedef signed char int5_t;
typedef unsigned char uint5_t;
#define INT5_MAX 15
#define INT5_MIN -16
#define UINT5_MAX 31

int5_t int5_add(int5_t a, int5_t b) {
    // converting signed to unsigned is defined
    // `% 32` is just the same as `& 0x1f`.
    const unsigned c = ((unsigned)a + (unsigned)b) % 32;
    // Just copying from the link...
    const unsigned m = 1U << (5 - 1);
    // Just two operations.
    return (c ^ m) - m;
}

void test(int a, int b, int c) {
    int d = int5_add(a, b);
    printf("%3hhd + %3hhd = %3hhd shouldbe %3hhd - %s\n", a, b, d, c, d == c ? "OK" : "ERROR");
}
int main() {
    test(-16, -1, 15);
    test(-16, 0,  -16);
    test(-16, 1,  -15);
    test(15,  1,  -16);
    test(15,  0,  15);
    test(15,  -1, 14);
    return -1;
}

更糟糕的是,左移这样的数字,或者左移“溢出”的正数是未定义的

标准中的“未定义”,但请参阅您的编译器文档。在gcc 上,我们“知道”integers implementation

GCC 只支持二进制补码整数类型,所有位模式都是普通值。

[...]

  • 对有符号整数(C90 6.3、C99 和 C11 6.5)的一些按位运算的结果。

位运算符作用于值的表示,包括符号位和值位,其中符号位被考虑 紧接在最高值位之上。带符号的“>>”作用于 通过符号扩展的负数。

作为 C 语言的扩展,GCC 不使用 C99 和 C11 中给出的纬度仅将带符号的“

【讨论】:

    猜你喜欢
    • 2021-12-21
    • 2018-03-25
    • 1970-01-01
    • 1970-01-01
    • 2014-01-30
    • 1970-01-01
    • 2017-07-17
    • 1970-01-01
    • 2015-04-12
    相关资源
    最近更新 更多