Как получить переполненную 1?
Чтобы сделать это впоследствии переносимым способом (не забывая, что unsigned int
может быть только 16 битами):
uint32_t a = 0Xffffffff;
uint32_t b = 0xffffffff;
uint32_t c_low = a + b;
uint32_t c_high;
if(c_low >= a) {
c_high = 0;
} else {
c_high = 1;
}
Чтобы сделать это заранее переносимым способом (без ответвлений):
uint32_t a = 0Xffffffff;
uint32_t b = 0xffffffff;
uint32_t c_low;
uint32_t c_high;
c_high = (a&b) >> 31;
c_low = (a ^ (c_high<<31)) + b;
Как сделать то же самое для умножения?
Умножение не нести, у него есть "верхняя половина". В частности, если вы умножите целое число без знака, которое имеет N битов, на целое число без знака, которое имеет M битов, то результат будет иметь N + M битов; и если оба числа имели одинаковый размер, то результат будет в два раза больше.
К сожалению, C не поддерживает «тип результата больше, чем тип источника / s», поэтому вам необходимо «предварительно продвигать "исходные типы, такие как:
uint32_t a = 0Xffffffff;
uint32_t b = 0xffffffff;
uint64_t temp = (uint64_t)a * (uint64_t)b;
uint32_t c_low = temp;
uint32_t c_high = temp >> 32;
Конечно, если компилятор не поддерживает больший тип, вы должны разделить его на более мелкие части, такие как:
uint32_t a = 0Xffffffff;
uint32_t b = 0xffffffff;
uint32_t a_low = a & 0xFFFF;
uint32_t a_high = a >> 16;
uint32_t b_low = a & 0xFFFF;
uint32_t b_high = b >> 16;
uint32_t temp_0 = a_low * b_low;
uint32_t temp_16a = a_high * b_low;
uint32_t temp_16b = a_low * b_high;
uint32_t temp_32 = a_high * b_high;
uint32_t c_low = temp_0 + (temp16a << 16) + (temp16b << 16);
uint32_t c_high = (temp16a >> 16) + (temp16b >> 16) + temp_32;
Как библиотеки bigint управляют этим?
Преимущественно; они используют встроенный язык ассемблера, потому что большинство процессоров поддерживают инструкции для эффективной работы с большими целыми числами и / или потому что вы можете получить прямой доступ к флагу переноса. Например; для 80х86; ЦП имеет adc
/ sbb
, shld
/ shrd
, mul
(с результатом двойной ширины) / div
(с числителем двойной ширины); плюс может быть расширения (adcx
и adox
).
На 32-битном языке ассемблера 80x86 добавление может выглядеть следующим образом:
xor edx,0
add eax,ebx ;c_low = a + b
adc edx,0 ;c_high = carry
.. и умножение может выглядеть следующим образом :
mul ebx ;edx:eax = a * b