Сборка АЦП (Добавить с переносом) в C ++ - PullRequest
16 голосов
/ 11 ноября 2010

Есть инструкция по сборке ADC. Я нашел это означает «Добавить с переносом». Но я не знаю, что означает . Или как написать эту инструкцию на C ++. И я знаю, что это не то же самое, что ADD. Поэтому делать простое суммирование не правильно.

INFO:
Скомпилировано в Windows. Я использую 32-битную установку Windows. Мой процессор - Core 2 Duo от Intel.

Ответы [ 6 ]

23 голосов
/ 11 ноября 2010

АЦП аналогичен ADD, но добавляет 1, если установлен флаг переноса процессора.

7 голосов
/ 11 ноября 2010

С здесь (сломан) или здесь

Однако процессор Intel имеет специальную инструкцию под названием adc. Эта команда ведет себя так же, как добавить команду. Единственная дополнительная вещь что он также добавляет значение флага переноса вместе. Так что это может быть очень удобно добавить большие целые числа. Предположим, вы хотите добавить 32-разрядные целые числа с 16-разрядными регистры. Как мы можем сделать это? Что ж, скажем, первое целое число держится на регистре пара DX: AX, и второй на BX: CX. Это как:

add  ax, cx
adc  dx, bx

Ах, во-первых, младший 16-бит добавляется добавить топор, CX. Тогда старший 16-битный добавлено с помощью adc вместо add. это потому что: если есть переполнение, бит переноса автоматически добавляется в чем выше 16 бит Итак, не громоздко проверка. Этот метод может быть расширен до 64 бит и т. д. Обратите внимание: если 32-разрядное целочисленное сложение переполняется тоже на старшем 16-битном, результат не будет правильно и флаг переноса установлен, например, Добавляем 5 миллиардов к 5 млрд.

С этого момента, помните, что это в значительной степени попадает в зону поведения, определяемого реализацией.

Вот небольшой пример, который работает для VS 2010 (32-разрядная версия, WinXp)

Предупреждение: $ 7.4 / 1- "Объявление asm условно поддерживается; его значение определяется реализацией. [Примечание: Обычно оно используется для передачи информации через реализацию ассемблеру. - Конечное примечание]"

int main(){
   bool carry = false;
   int x = 0xffffffff + 0xffffffff;
   __asm {
      jc setcarry
setcarry:
      mov carry, 1
   }
}
6 голосов
/ 11 ноября 2010

Поведение АЦП можно моделировать как на C, так и на C ++. В следующем примере добавляются два числа (сохраняются как массивы без знака, так как они слишком велики, чтобы поместиться в один без знака).

unsigned first[10];
unsigned second[10];
unsigned result[11];

....   /* first and second get defined */

unsigned carry = 0;
for (i = 0; i < 10; i++) {
    result[i] = first[i] + second[i] + carry;
    carry = (first[i] > result[i]);
}
result[10] = carry;

Надеюсь, это поможет.

4 голосов
/ 10 июня 2016

Язык C ++ не имеет никакого понятия флага переноса, поэтому создание встроенной функции-оболочки вокруг инструкции ADC неуклюже. Однако Intel все равно это сделала: unsigned char _addcarry_u32 (unsigned char c_in, unsigned a, unsigned b, unsigned * out);. В последний раз, когда я проверял, gcc справился с этим плохо (сохранив результат переноса в целочисленный регистр вместо того, чтобы оставить его в CF), но, надеюсь, собственный компилятор Intel справится с этим лучше.

См. Также вики-тег для документации по сборке.


Компилятор будет использовать для вас ADC при добавлении целых чисел, превышающих один регистр, например добавление int64_t в 32-битном коде или __int128_t в 64-битном коде.

#include <stdint.h>
#ifdef __x86_64__
__int128_t add128(__int128_t a, __int128_t b) { return a+b; }
#endif
    # clang 3.8 -O3  for x86-64, SystemV ABI.
    # __int128_t args passed in 2 regs each, and returned in rdx:rax
    add     rdi, rdx
    adc     rsi, rcx
    mov     rax, rdi
    mov     rdx, rsi
    ret

вывод asm из проводника компилятора Godbolt . -fverbose-asm от clang не очень вежливый, но gcc 5.3 / 6.1 тратит впустую две mov инструкции, поэтому он менее читабелен.

4 голосов
/ 20 января 2011

В этом есть ошибка. Попробуйте этот ввод:

unsigned first[10] =  {0x00000001};
unsigned second[10] = {0xffffffff, 0xffffffff};

Результат должен быть {0, 0, 1, ...}, но результат равен {0, 0, 0, ...}

Изменение этой строки:

carry = (first[i] > result[i]);

к этому:

if (carry)
    carry = (first[i] >= result[i]);
else
    carry = (first[i] > result[i]);

исправляет это.

1 голос
/ 17 февраля 2017
unsigned long result;
unsigned int first;
unsigned int second;

result = first + second;
result += (result & 0x10000) ? 1 : 0;
result &= 0xFFFF
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...