Отличается 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, если вы чувствуете себя смелым. (Это будет работать до тех пор, пока подписанные целые числа представлены дополнением до двух, что обычно имеет место, но это не гарантируется стандартом)

Другие вопросы по тегам