Чтобы получить число по модулю 31, вам просто нужно суммировать цифры в основание 32, точно так же, как вы вычисляете по модулю 3 и 9 десятичного числа
unsigned mod31(std::bitset<74> b) {
unsigned mod = 0;
while (!b.none()) {
mod += (b & std::bitset<74>(0x1F)).to_ulong();
b >>= 5;
}
while (mod > 31)
mod = (mod >> 5) + (mod & 0x1F);
return mod;
}
Вы можете ускорить расчет по модулю, запустив дополнения параллельно, например как это сделано здесь. Аналогичный метод можно использовать для вычисления по модулю 3, 5, 7, 15... и 231 - 1.
Однако, поскольку вопрос на самом деле касается базового преобразования, а не модуля, как сказано в заголовке, для этой цели вам нужно выполнить реальное деление. Обратите внимание, что 1/b равно 0.(1) по основанию b + 1, мы имеем
1/31 = 0,000010000100001000010000100001...32 = 0.(00001)32
и тогда N/31 можно рассчитать так
N/31 = N×2-5 + N×2-10 + N×2-15 + ...
uint128_t result = 0;
while (x)
{
x >>= 5;
result += x;
}
Поскольку и по модулю, и по делению используется сдвиг на 5, вы также можете выполнить их вместе в одном цикле.
Однако сложная часть здесь заключается в том, как правильно округлить частное. Вышеупомянутый метод будет работать для большинства значений, за исключением некоторых между кратным 31 и следующей степенью 2. Я нашел способ исправить результат для значений до нескольких тысяч, но еще не нашел общий способ для всех значений
Вы можете видеть, что тот же метод сдвига и добавления используется для деления на 10 и на 3. В знаменитом Hacker's Delight есть еще примеры с правильным округлением. У меня не было достаточно времени, чтобы прочитать книгу, чтобы понять, как они реализуют часть коррекции результатов, поэтому, возможно, я вернусь к этому позже. Если у кого-то есть идеи, как это сделать, буду признателен.
Одно предложение состоит в том, чтобы сделать деление в фиксированной точке. Просто сдвиньте значение влево, чтобы у нас было достаточно дробной части для последующего округления.
uint128_t result = 0;
const unsigned num_fraction = 125 - 75 // 125 and 75 are the nearest multiple of 5
// or maybe 128 - 74 will also work
uint128_t x = UFI_Number << num_fraction;
while (x)
{
x >>= 5;
result += x;
}
// shift the result back and add the fractional bit to round
result = (result >> num_fraction) + ((result >> (num_fraction - 1)) & 1)
Обратите внимание, что ваш результат выше неверен. Я подтвердил, что результат CEOPPJ62MK6CPR1 из обоих ответа Янива Шакеда и Wolfra разные символы для цифр
person
phuclv
schedule
26.07.2018
__int128
в GCC. Есть ли он в вашем компиляторе? Если это так, вы можете преобразовать свой набор битов в целое число этого типа (побитно или с помощью reinterpret_casts, хотя в последнем случае будьте осторожны с прямым/обратным порядком байтов) и выполнить деление целых чисел напрямую. - person Ivan Smirnov   schedule 26.07.2018__int128_t
. - person Cheers and hth. - Alf   schedule 26.07.2018__int128_t
, но мой последний ответ неверен, мы не в 2015, а в 2010 году (я знаю, что это старо). - person thibsc   schedule 26.07.2018__int128_t
больше не работает с Visual C++ 2017, а значит, и раньше не работало. Хм! Во всяком случае, я приготовил некоторый код, опубликовав ответ сейчас. - person Cheers and hth. - Alf   schedule 26.07.2018