Ответ 1
АЦП совпадает с ADD, но добавляет дополнительный 1, если установлен флаг переноса процессора.
Существует инструкция по сборке ADC
. Я нашел, что это означает "Добавить с переносом". Но я не знаю, что это значит. Или как написать эту инструкцию на С++. И я знаю, что это не то же самое, что ADD
. Поэтому сделать простое суммирование неверно.
INFO:
Составлено в Windows. Я использую 32-разрядную установку Windows. Мой процессор Core 2 Duo от Intel.
АЦП совпадает с ADD, но добавляет дополнительный 1, если установлен флаг переноса процессора.
Поведение ADC можно моделировать как на 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;
Надеюсь, что это поможет.
Однако процессор Intel имеет специальную инструкцию под названием adc. Эта команда ведет себя аналогично добавьте команду. Единственная дополнительная вещь что он также добавляет флаг переноса значения вдоль. Таким образом, это может быть очень удобно для добавьте большие целые числа. Предположим, вы хотите для добавления 32-разрядных целых чисел с 16-разрядными регистры. Как мы можем сделать это? Что ж, скажем, что первое целое число проведенных на паре регистров DX: AX и второй - на BX: CX. Это как:
add ax, cx adc dx, bx
Ah, так что сначала нижний 16-бит добавляется добавить ax, cx. Тогда более высокий 16-бит добавлен с помощью adc вместо add. это потому что: если есть переполнения, бит переноса автоматически добавляется в более высокий 16-бит. Таким образом, без громоздких проверка. Этот метод может быть расширен до 64 бит и т.д. Обратите внимание: если переполнение 32-битных целых чисел тоже на более высоком 16-битном, результат не будет правильным, а флаг переноса установлен, например. Добавление 5 миллиардов к 5 млрд.
Все здесь, помните, что он в значительной степени попадает в зону реализации, определяемую реализацией.
Вот небольшой пример, который работает для VS 2010 (32-разрядный, WinXp)
Предостережение: $7.4/1- "Объявление asm условно поддерживается, его значение определяется реализацией. [Примечание: обычно используется для передачи информации через реализацию ассемблеру. -end note]"
int main(){
bool carry = false;
int x = 0xffffffff + 0xffffffff;
__asm {
jc setcarry
setcarry:
mov carry, 1
}
}
В этом есть ошибка. Попробуйте этот ввод:
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]);
исправляет его.
Язык С++ не имеет понятия флага переноса, поэтому создание обертки внутренней функции вокруг ADC
инструкция неуклюжий. Однако Intel все равно: unsigned char _addcarry_u32 (unsigned char c_in, unsigned a, unsigned b, unsigned * out);
. Последнее, что я проверил, gcc сделал плохую работу с этим (сохранение результата переноса в целочисленный регистр, вместо того, чтобы оставить его в CF), но, надеюсь, собственный компилятор Intel делает лучше.
См. также x86 tag wiki для документации по сборке.
Компилятор будет использовать 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 explorer компилятора. clang -fverbose-asm
не очень вегетативна, но gcc 5.3/6.1 тратит две инструкции mov
, поэтому она менее читаема.
unsigned long result;
unsigned int first;
unsigned int second;
result = first + second;
result += (result & 0x10000) ? 1 : 0;
result &= 0xFFFF