Отличается 32-битным приведением в long / __ int64, почему? - PullRequest
0 голосов
/ 27 ноября 2009

Я пишу свою маленькую библиотеку с множественными точностями, и при написании метода для вычитания я обнаружил странную ошибку. Вот блок кода, для которого я написал для вычитания мультиточности:

/* 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-битное без знака.

Допустим, мы вычитаем 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 ]

3 голосов
/ 27 ноября 2009

Что sizeof(long) в вашей среде? Я подозреваю, что если вы проверите, вы обнаружите, что это 4, т. Е. Ваши unsigned long на самом деле являются 32-битными значениями.

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

...