在二进制中,从有符号幅度转换为二进制补码。全部在C.

时间:2014-02-20 20:08:36

标签: c bit puzzle bits limits

我有一个有趣的谜题。我需要从带符号的数字转换,比如说-5(0x80000005)到2的补码-5(0xFFFFFFFB)。我可以使用的唯一操作符是,!,〜,&,^,|,+,<<,>>并且最多15个与我想要的变量任意组合。 =可以使用,不计入运营商总数。此外,不能使用“if”语句,循环或对任何类型的函数的调用。

该功能看起来像

int sign2two(int x){
//... some stuff here
return x;
}

我遇到的问题是我可以查明数字是否为负数。但我想说,如果num为负,则翻转位并添加1.否则返回数字。我无法弄清楚如何做到这一点。感谢。

3 个答案:

答案 0 :(得分:2)

我认为这些问题非常愚蠢......

#include <stdint.h>
#include <stdio.h>

int32_t test1(uint32_t x) {
    const uint32_t sign  = (x & 0x80000000); // Total operations: 1
    const uint32_t value = (x & 0x7FFFFFFF); // Total operations: 2

    // Let's create a value N, where N is equal to:
    // if (sign)
    //     N = 0xFFFFFFFF;
    // else
    //     N = 0x00000000;
    uint32_t N = sign;  // Total operations: 2
    N = N | (N >> 1);   // Total operations: 4 
    N = N | (N >> 2);   // Total operations: 6
    N = N | (N >> 4);   // Total operations: 8
    N = N | (N >> 8);   // Total operations: 10
    N = N | (N >> 16);  // Total operations: 12

    // Let's create a value MaybeOne, where MaybeOne is equal to:
    // if (sign)
    //     MaybeOne = 1;
    // else
    //     MaybeOne = 0;
    uint32_t MaybeOne = N & 0x1; // Total operations: 13

    // Now, Let's perform the following step:
    // if (sign)
    //     return (value ^ 0xFFFFFFFF) + 1;
    // else
    //     return (value ^ 0x00000000) + 0;
    return (value ^ N) + MaybeOne; // Total operations: 15
}

int32_t test2(uint32_t x) {
    int32_t sign = (x & 0x80000000);
    int32_t value = (x & 0x7FFFFFFF);

    if (sign)
        return -value;
    else
        return value;
}

int main() {
    uint32_t i;
    for (i=0; i<0xFFFFFFFF; ++i) 
        if (test1(i) != test2(i))
            printf("0x%08x\n", i);
}

另外,使用Eric Postpischil的评论,我们可以大幅减少操作次数:

int32_t test3(uint32_t x) {
    const uint32_t sign  = (x >> 31);        // Total operations: 1
    const uint32_t value = (x & 0x7FFFFFFF); // Total operations: 2

    // Let's create a value N, where N is equal to:
    // if (sign)
    //     N = 0xFFFFFFFF;
    // else
    //     N = 0x00000000;
    const uint32_t N = ~sign + 1;  // Total operations: 4

    // Let's create a value MaybeOne, where MaybeOne is equal to:
    // if (sign)
    //     MaybeOne = 1;
    // else
    //     MaybeOne = 0;
    uint32_t MaybeOne = sign; // Total operations: 4

    // Now, Let's perform the following step:
    // if (sign)
    //     return (value ^ 0xFFFFFFFF) + 1;
    // else
    //     return (value ^ 0x00000000) + 0;
    return (value ^ N) + MaybeOne; // Total operations: 6
}

答案 1 :(得分:2)

您没有可用的条件,但您可以屏蔽。根据符号的值创建掩码有不同的方法。

以下使用 Eric Postpischil 的非常好的提示!

如果符号为0(即为正),则掩码将变为0,否则它仍然是您需要的值 - &gt;条件无条件

#include <stdarg.h>
#include <stdio.h>
int sign2two(unsigned int a){
    int sign = a>>31; // alternative: sign = !!((a<<1>>1)+~a+1);  see below
    unsigned int withoutSign = a << 1 >> 1;
    unsigned int mask = ~sign +1;
    printf("withoutSign is: %u\nsign is: %u\nmask is: %x\n", 
        withoutSign, sign, mask);
    unsigned int temp = (withoutSign^mask)+sign;
    return *(int*)&temp;
}
void printSign2two(unsigned int a){
    printf("the 2s complement number: %i\n\n", sign2two(a));
}
int main(){
    printSign2two(0x00000005);
    printSign2two(0x80000005);
    return 0;
}

输出:

withoutSign is: 5
sign is: 0
mask is: 0
the 2s complement number: 5

withoutSign is: 5
sign is: 1
mask is: ffffffff
the 2s complement number: -5
关于sign = !!((a<<1>>1)+~a+1);

注意。这使用了比sign = a>>31更多的运算符,但它与32/64位数无关。

  • <<1>>1清除最重要的位,
  • 然后我们要检查它是否与原始版本不同。由于-a不允许,我们会使用+~a+1(再次感谢 Eric Postpischil
  • 如果没有差异,我们已经有了我们想要的值0,否则我们有一些巨大的数字
  • 然后我们用!!将巨大的数字改为1。

答案 2 :(得分:1)

要将Sign Magnitude x转换为Two's Complement y:

1)在双补机上 2)仅使用!,〜,&amp;,^,|,+,&lt;&lt;,&gt;&gt;
3)不使用?:, - ,*,/
4)不假设4字节int
5)适用于所有Sign Magnitude整数,包括+0和-0

#include <limits.h>
int sm2tc(int x) {
  int sign = x & INT_MIN;
  int negmask = UINT_MAX + !sign;
  return (x & ~negmask) | (negmask & ((~x + 1)^INT_MIN));
}

这个答案是一个重复的问题How to convert from sign-magnitude to two's complement,其选定的答案不符合OP规定的考虑操作限制的标准。