如何在 5 位表示上模拟二进制补码算法?

问题描述

假设一个人必须将两个有符号数相加(或相减等),如下编码:

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);
    }

我需要模拟相同的效果,但分别用有符号字节和(理论上的)5 位有符号整数替换 int 和 short。类似于以下内容

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)));
    }

虽然上面的代码已经完成了这个技巧,但它有一些缺陷:右移负数是实现定义的,更糟糕​​的是,左移这样的数字,或者左移“溢出”的正数是不明确的。所以代码既不可移植也不真正可预测。

这就是我的问题:如何通过快速、可移植且定义明确的位操作来达到预期的效果

提前致谢。

解决方法

使用 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,-16);
    test(-16,1,-15);
    test(15,-16);
    test(15,15);
    test(15,14);
    return -1;
}

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

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

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

[...]

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

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

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