В двоичном формате преобразовать величину со знаком в дополнение до двух. Все на С

У меня есть интересная загадка. Мне нужно преобразовать число со знаком, скажем, -5 (0x80000005) в дополнение до двух -5 (0xFFFFFFFFB). Единственные операторы, которые я могу использовать, это !, ~, &, ^, |, +, ‹‹, >> и не более 15 из них в любой комбинации с любым количеством переменных, которое я хочу. = может использоваться и не учитывается при подсчете общего количества операторов. Кроме того, нельзя использовать операторы if, циклы или вызовы функций любого типа.

функция будет выглядеть

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

Проблема, с которой я сталкиваюсь, заключается в том, что я могу узнать, является ли число отрицательным. Но я хочу сказать, что если число отрицательное, то переверните биты и добавьте 1. В противном случае верните число. И я не могу понять, как это сделать. Спасибо.


person Shaw    schedule 20.02.2014    source источник
comment
&& не является оператором, который мне разрешено использовать.   -  person Shaw    schedule 21.02.2014
comment
Почему? Потому что процессор, на котором вы работаете, не поддерживает это, или потому что шляпа является правилом домашнего задания?   -  person Nils Pipenbrinck    schedule 21.02.2014
comment
вот как сделать противоположное stackoverflow. com/questions/19689586/ , немного усилий с вашей стороны, и все будет хорошо.   -  person user2485710    schedule 21.02.2014


Ответы (3)


Чтобы преобразовать величину знака x в дополнение до двух y:

1) На машине с дополнением до двух.
2) Используются только !, ~, &, ^, |, +, ‹‹, >>
3) Не используются ?:, -, *, /
> 4) Не предполагает 4-байтовый тип int
5) Работает со всеми целыми числами величины знака, включая +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));
}

Этот ответ относится к повторяющемуся вопросу Как преобразовать из знака-величины до дополнения до двух , выбранный ответ которого не соответствовал заявленным критериям OP с учетом ограничения операции.

person chux - Reinstate Monica    schedule 20.02.2014
comment
Что произойдет, если будет передано 0x8000000? Ваш код, кажется, возвращает 0x80000000. - person Shaw; 22.02.2014
comment
@Benb, вы имеете в виду 0x8000000 (0x8 + 6 нулей), как в вашем комментарии, код возвращает 0x8000000 (0x8 + 6 нулей), как и должно быть. Если вы имеете в виду 0x80000000 (0x8 + 7 нулей), код возвращает 0, как и должно быть на 32-битном int. Проверено повторным тестом. Если вы работаете с 64-битным целым числом, то 0x80000000 (0x8 + 7 нулей) должно возвращать 0x80000000 (0x8 + 7 нулей). - person chux - Reinstate Monica; 22.02.2014
comment
Я прошу прощения. Я сделал ошибку при создании своего UMAX. Спасибо, вы заслужили выбранное решение. - person Shaw; 23.02.2014

Эти проблемы крайне глупы, на мой взгляд...

#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
}
person Bill Lynch    schedule 20.02.2014
comment
Глядя на второй ответ, какова функция MaybeOne? Почему нельзя просто использовать знак? Кроме того, я не упомянул об этом, но единственными типами переменных, которые можно использовать, являются обычные типы int. - person Shaw; 22.02.2014
comment
В моем тестере второй ответ работает, пока я не попробую 0xffffffff. Я получаю возврат 0x7ffffffd, когда он должен быть 0x80000001. - person Shaw; 22.02.2014
comment
На бумаге это также работает как 0x7ffffffd. Спасибо за вашу помощь! - person Shaw; 22.02.2014
comment
Вручную, 0xFFFF_FFFF == -(0x7FFF_FFFFF). Итак, sign2two(0xFFFFFFFF) == -(2147483647). Если бы мы преобразовали 0x80000001 как число с дополнением до двух в десятичное число, мы также получили бы -2147483647. Таким образом, правильная операция (которую выполняет приведенный выше код) — test1(0xFFFFFFFF) == 0x80000001. - person Bill Lynch; 22.02.2014

У вас нет доступных условий, но вы можете маскировать. Существуют различные способы создания маски в зависимости от значения знака.

Далее используются очень полезные советы Эрика Постпишила!

Если знак равен 0 (т.е. положительный), то маска станет равной 0, в противном случае это все еще нужное вам значение -> условное без условного

#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 (еще раз спасибо, Эрик Постпишил)
  • если нет разницы, у нас уже есть значение 0, которое мы хотим, иначе у нас есть какое-то огромное число
  • мы тогда используем !! изменить огромное число на 1.
person Bernd Elkemann    schedule 20.02.2014
comment
Технически это выходит за рамки вопроса, потому что Бенбу не разрешено использовать оператор умножения. - person Bill Lynch; 21.02.2014
comment
@EricPostpischil очень хорошие советы! Я использовал их, надеюсь, вы не против. Если бы я был на вашем месте, я бы ответил по-своему. Я постараюсь найти что-нибудь, чтобы избавиться от 31. - person Bernd Elkemann; 21.02.2014
comment
знак = !!((а‹‹1››1)-а); - person Bernd Elkemann; 21.02.2014
comment
Пока это мой фаворит: unsigned int m = a ^ a<<1>>1, z = !m + ~0u; return a+z ^ z>>1;. - person Eric Postpischil; 21.02.2014
comment
@EricPostpischil хорошо. вы должны дать ответ и объяснить шаги мне и другим :-) - person Bernd Elkemann; 21.02.2014