Отличается 32-битным приведением в long/__int64, почему?
Я пишу свою собственную маленькую библиотеку с множественными точностями, и при написании метода вычитания я обнаружил странную ошибку. Вот блок кода, для которого я написал для вычитания мультиточности:
/* subtraction */
for (; p_aReverseIter != a.m_elements.rend(); ++p_aReverseIter, ++p_bReverseIter)
{
temp = static_cast<__int64>(static_cast<__int64>(p_aReverseIter->m_Value) -
static_cast<__int64>(p_bReverseIter->m_Value) +
(carry));
--- debug output-
p_aReverseIter->m_Value = static_cast<unsigned int>(temp & 0xffffffff);
carry = static_cast<unsigned long>(temp >> 32);
}
p_aReverseIter-> m_Value - это 32-битное целое число без знака, а a,b - BigInt. Значения хранятся внутри вектора в стиле Big Endian. temp - это __int64, и значение carry должно работать как 32-битный unsigned long.
Допустим, мы вычитаем b из a, a > b (вычитание без знака), но все 32-битные слова в b больше, чем a. Эта процедура производит следующий вывод:
a = 0xfefefefe (10 elem) 0xfefefefe (10 elem) 0xfefefefe (10 elem)
0xfefefefe (10 elem)
b = 0x12 (2 elem) 0x12121212 (9 elem) 0x12121212 (9 elem) 0x12121212
(9 elem) 0x12121212 (9 elem)
a[i]: 12121212
b[i]: fefefefe
old carry: 0
temp = a - b + carry: ffffffff13131314
Value: 13131314
new carry: ffffffffffffffff
a[i]: 12121212
b[i]: fefefefe
old carry: ffffffff
temp = a - b + carry: 13131313
Value: 13131313
new carry: 0
a[i]: 12121212
b[i]: fefefefe
old carry: 0
temp = a - b + carry: ffffffff13131314
Value: 13131314
new carry: ffffffffffffffff
a[i]: 12121212
b[i]: fefefefe
old carry: ffffffff
temp = a - b + carry: 13131313
Value: 13131313
new carry: 0
...
Но перенос всегда должен быть 0xfffffffff. Каждый раз, когда он равен нулю, результатом является "13131314", что неверно. Теперь давайте изменим перенос с unsigned long на unsigned __int64 и
carry = static_cast<unsigned long>(temp >> 32);
в
carry = static_cast<unsigned __int64>(temp >> 32);
Теперь перенос всегда рассчитывается правильно и имеет значение 0xffffffff. Но смещение прав на 64-битное значение 2^32 всегда должно давать 32-битный результат.
Мой вопрос: чтобы понять разные результаты, чего мне не хватает?
Большое спасибо.
2 ответа
Какой sizeof(long)
в вашей среде? Я подозреваю, что если вы проверите, вы найдете это 4, то есть ваш unsigned long
на самом деле 32-битные значения.
p_aReverseIter->m_Value = static_cast<unsigned int>(temp & 0xffffffff);
carry = static_cast<unsigned long>(temp >> 32);
Не используйте жесткие значения, подобные этим. Вы не гарантированы, что unsigned long
это любой конкретный размер (и он часто не будет 64-битным, как вы предполагаете). Так что битовое смещение, а также ваши побитовые и должны принимать это во внимание. Вы можете заменить 32 на что-то вроде sizeof(unsigned long)*8
возможно. И вместо 0xffffffff
, ~0L
сделал бы трюк, или, возможно, -1, если вы чувствуете себя смелым. (Это будет работать до тех пор, пока подписанные целые числа представлены дополнением до двух, что обычно имеет место, но это не гарантируется стандартом)