Написать a = (a == b ? 0 : a) без ветвлений

Тема в разделе "LANGS.C", создана пользователем Ation, 6 июл 2010.

  1. Ation

    Ation New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2005
    Сообщения:
    92
    Адрес:
    Zaporozhie
    Суть в заголовке. Есть у кого идеи, как написать на С этот код, без ветвлений?
    Сравнения тоже использовать нельзя, тоесть (a == b) * a не катит.
    Разрядность a и b одинакова, и известна на этапе компиляции.
     
  2. J0E

    J0E New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2008
    Сообщения:
    621
    Адрес:
    Panama
    А смотрел код после компилятора?
    Что значит нельзя сравнения? А если будет sub или xor?

    GCC должен использовать CMOVcc, VC -- SBC + AND

    Вариант без сравнений (использования флагов)
    c = a ^ b;
    c |= c << 1;
    c |= c << 2;
    c |= c << 4;
    c |= c << 8;
    a = a & c;

    :)
     
  3. google

    google New Member

    Публикаций:
    0
    Регистрация:
    10 авг 2007
    Сообщения:
    140
    Это выдает MSVS. Ни сравнений, ни ветвлений :)
    Код (Text):
    1. mov     eax, a
    2. mov     ecx, b
    3. mov     edx, eax
    4. sub     edx, ecx
    5. neg     edx
    6. sbb     edx, edx
    7. and     eax, edx
     
  4. Ation

    Ation New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2005
    Сообщения:
    92
    Адрес:
    Zaporozhie
    J0E
    код не будет работать.
    например для a= 11b, и b = 1b.
    Под сравнениями я понимаю операции == и !=
     
  5. Ation

    Ation New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2005
    Сообщения:
    92
    Адрес:
    Zaporozhie
    google
    Этот код работает, но суть именно в том чтоб на С написать )
    Есть же еще ARM, есть еще MIPS )
     
  6. google

    google New Member

    Публикаций:
    0
    Регистрация:
    10 авг 2007
    Сообщения:
    140
    Ation
    Ну вот, а я только хотел решение предложить...
    Код (Text):
    1. #include <intrin.h>
    2. // ...
    3.     unsigned long   tmp;
    4.     a &= -_BitScanForward(&tmp, a-b);
     
  7. Ation

    Ation New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2005
    Сообщения:
    92
    Адрес:
    Zaporozhie
    у меня пока есть только такой вариант, но мне он не нравистся умножением )
    char c;
    char offset = sizeof(a) * 8;

    c = ((a - b) & (1 << offset )) >> offset ;
    c |= ((b - a) & (1 << offset )) >> offset ;

    a *= c;
     
  8. google

    google New Member

    Публикаций:
    0
    Регистрация:
    10 авг 2007
    Сообщения:
    140
    Во:
    Код (Text):
    1. a &= ((signed)((a-b) | (b-a)) >> sizeof(a)*8)
    ADD:
    Но всё же, IMHO, лучше оставить изначальный вариант, оставив оптимизацию компилятору.
     
  9. Ation

    Ation New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2005
    Сообщения:
    92
    Адрес:
    Zaporozhie
    google
    Млин, супер )
     
  10. Ation

    Ation New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2005
    Сообщения:
    92
    Адрес:
    Zaporozhie
    Это просто for fun, конечно лучше оставить читабельный вариант )
     
  11. Ation

    Ation New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2005
    Сообщения:
    92
    Адрес:
    Zaporozhie
    google
    так, что-то я напорол с тестами походу...
     
  12. J0E

    J0E New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2008
    Сообщения:
    621
    Адрес:
    Panama
    Чего это не будет? с = 3 ^ 1; // == 2 и далее сдвигами должно получиться ~0 при and с которым другой операнд сохраняется. Эту ошибку исправь сам :) А если a эквивалентно b то будет 0.
    То есть > не сравнения?
     
  13. J0E

    J0E New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2008
    Сообщения:
    621
    Адрес:
    Panama
    SUB это и есть сравнение, далее выставляется Carry Flag который далее расширяется SBB
     
  14. Ation

    Ation New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2005
    Сообщения:
    92
    Адрес:
    Zaporozhie
    J0E
    c = a ^ b ; // 10b
    c |= c << 1; // 110b
    ...
    .. с = 1111 1110, и флаг переполнения.
    a &= c; // 0
    Задача написать именно на С, а там нет средств следить на переполнением, если мне не изменяет память.
     
  15. google

    google New Member

    Публикаций:
    0
    Регистрация:
    10 авг 2007
    Сообщения:
    140
    Эмм, не понял...
     
  16. J0E

    J0E New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2008
    Сообщения:
    621
    Адрес:
    Panama
    Переполнение не нужно. c |= c << 1 | с >> а_какое_число_писать_здесь_не_сказано;
    Перед тем как искать ошибки в ответах, необходимо сформулировать задачу. На определенном множестве мой алгоритм без ошибочен :)
     
  17. Ation

    Ation New Member

    Публикаций:
    0
    Регистрация:
    6 авг 2005
    Сообщения:
    92
    Адрес:
    Zaporozhie
    Не обращай внимания, то мне показалось что при беззнаковых числах, и установленым последним битом может не работать, и на тестах я это увидел, но потом увидел ошибку в тестах )
     
  18. skomarov

    skomarov New Member

    Публикаций:
    0
    Регистрация:
    14 май 2008
    Сообщения:
    389
    Ation
    На языке Си. Для решения используется операция ! логического НЕ.
    Код (Text):
    1. a &= (!(a^b)-1);
    2.  
    3. // a == b  =>  a^b == 0  =>  !(a^b) == 1  =>  (!(a^b) - 1) == 0           =>  a &= 0          == 0
    4. // a != b  =>  a^b != 0  =>  !(a^b) == 0  =>  (!(a^b) - 1) == 0xFFFFFFFF  =>  a &= 0xFFFFFFFF == a
    На языке ассемблер. Решение аналогичное выше приведенному. Операция сравнения появляется в результате синергии совместного использования инструкций xor и setz.
    Код (Text):
    1. ; eax - содержит значение a
    2. ; edx - содержит значение b
    3.  
    4. xor ecx, ecx
    5. xor edx, eax ; b^a
    6.  
    7. setz cl
    8. ; a == b  =>  b^a == 0  =>  ZF == 1  =>  CL = 1
    9. ; a != b  =>  b^a != 0  =>  ZF == 0  =>  CL = 0
    10.  
    11. dec ecx
    12. ; a == b  =>  ecx = 0
    13. ; a != b  =>  ecx = 0xFFFFFFFF
    14.  
    15. and eax, ecx
    Решение на языке Си стало следствием решения на языке ассемблер, но оказалось, что аналогичный код в процессе компиляции создает Borland C++ Builder.
     
  19. google

    google New Member

    Публикаций:
    0
    Регистрация:
    10 авг 2007
    Сообщения:
    140
    skomarov
    Операция "не" в результате дает либо TRUE либо FALSE, причем TRUE не обязательно == 1. Поэтому вычитать единицу из результата логической операции не правильно.
     
  20. skomarov

    skomarov New Member

    Публикаций:
    0
    Регистрация:
    14 май 2008
    Сообщения:
    389
    google
    Это замечание следует из стандарта?

    Код (Text):
    1. WG14/N1124 ISO/IEC 9899:TC2 Committee Draft — May 6, 2005
    2.  
    3. 6.5.3 Unary operators
    4. 6.5.3.3 Unary arithmetic operators
    5.  
    6. 5. The result of the logical negation operator ! is 0 if the value of its operand compares
    7. unequal to 0, 1 if the value of its operand compares equal to 0. The result has type int.
    8. The expression !E is equivalent to (0==E).