Эффективное 128-битное сложение с использованием флага переноса

Я использую 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, чтобы заставить компилятор использовать внутренний код операции?

39
задан Randall Meyers 12 July 2011 в 04:12
поделиться