Я использую 128-битный целочисленный счетчик в самых внутренних циклах моего кода C ++. (Неактуальный фон: фактический приложение оценивает конечно-разностные уравнения на регулярной сетке, которая включает в себя повторяющееся увеличение больших целых чисел, и даже 64 бита недостаточно точности, потому что небольшое округление накапливается достаточно, чтобы повлиять на ответы.)
Я представил целое число как два 64 bit unsigned longs. Теперь мне нужно увеличить эти значения на 128-битную константу. Это несложно, но вам нужно вручную перехватить перенос из младшего слова в старшее.
У меня есть рабочий код примерно такого рода :
inline void increment128(unsigned long &hiWord, unsigned long &loWord)
{
const unsigned long hiAdd=0x0000062DE49B5241;
const unsigned long loAdd=0x85DC198BCDD714BA;
loWord += loAdd;
if (loWord < loAdd) ++hiWord; // test_and_add_carry
hiWord += hiAdd;
}
Это жесткий и простой код. Он работает.
К сожалению, это около 20% моего времени выполнения. Убийственная строка - это тест loWord. Если я его удалю, я, очевидно, получу неправильные ответы, но накладные расходы времени выполнения падает с 20% до 4%! Так что переносить тест i особенно дорого!
Мой вопрос: выставляет ли C ++ аппаратные флаги переноса, даже как расширение GCC? Похоже, что добавления можно было бы сделать без строки test-and-add-carry выше, если бы в реальных скомпилированных инструкциях использовалось добавление с использованием последней инструкции переноса для добавления hiWord. Есть ли способ переписать строку test-and-add-carry, чтобы заставить компилятор использовать внутренний код операции?