【问题标题】:Different 32bit-cast into long/__int64, why?不同的 32 位转换为 long/__int64,为什么?
【发布时间】:2010-12-20 23:52:20
【问题描述】:

我正在编写自己的小型多精度库,在编写减法方法时,遇到了一些奇怪的错误。这是我为多精度减法编写的代码块:

/* 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 并且进位应该作为 32 位无符号长。

假设我们从 a 中减去 b,a > b(无符号减法),但是 b 中的所有 32 位字都比 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。 但是将 2^32 的 64 位值右移应该总是产生 32 位的结果。

我的问题是:要了解不同的结果,我缺少什么?

非常感谢。

【问题讨论】:

  • 小注:可以使用标准的long long代替非标准的__int64
  • long long 在 C++ 中也不是标准的。它是在C++99中定义的,计划在C++0x中添加到C++中,但目前还没有。
  • @Zyrkon:抱歉,您的描述毫无意义。如果我们正在执行无符号减法和 A > B,如您所说,B 中的所有单词不能大于 A 中的所有单词。此外,在您的示例中,您的逐个元素数据与数组数据(A 和B交换)。请先把事情整理好,然后我们才能开始理解这一切。
  • 这应该是以社区wiki为例,如何不使用c++。关于您的问题,不要混合使用 __int64、long 和 int。如果您需要具有精确定义宽度的类型,请因此使用 __int64(或更好的来自外部 stdint.h 的 uin64_t,这在 vc++ 上是缺失的)并完全避免使用 long 甚至 unsigned int。

标签: c++ bignum


【解决方案1】:

您的环境中的sizeof(long) 是什么?我怀疑如果你测试你会发现它是 4,即你的 unsigned long 实际上是 32 位值。

【讨论】:

  • sizeof long 定义为 4。 sizeof int 是可变的
  • @Ruben:根据定义?你在说什么“定义”?
  • 我的编译器显示 sizeof(long) == 8。您刚刚发现了一个显着的错误,如果您考虑 gcc 用于生产 64 位软件的时间,则必须隐藏 10 年以上。
【解决方案2】:
  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。 (只要有符号整数由二进制补码表示,它就可以工作,通常就是这种情况,但标准不保证)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-08
    • 2013-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-16
    • 2019-06-21
    相关资源
    最近更新 更多