这篇文章与我以前的帖子有关:
Weird data corruption in C++ code for unsigned long long
该帖子中提出的问题已得到解决,但现在我面临与截断无符号长long数据类型相关的其他问题。这发生在函数iPow()中,用于将基数提升为指数的幂。 iPow()的源代码在我之前的帖子中共享,但为了方便起见,下面转载:
unsigned long long userinput::iPow(){
while(this->revValue)
{
if (this->revValue & 1ULL)
{
this->result *= this->value;
}
this->revValue >>= 1;
this->value *= this->value;
}
return this->result;
}
在上面的函数中,value,revValue和result都是unsigned long long。类声明的源代码在上一篇文章中共享,但为方便起见,下面转载:
class userinput{
public:
userinput(unsigned long long);
~userinput();
unsigned long long reverse ();
unsigned long long raise_to_reverse ();
private:
userinput();
userinput(const userinput&);
void operator=(const userinput&);
private:
unsigned long long value;
unsigned int numDigits;
unsigned long long revValue;
unsigned long long result;
private:
void calc_numDigits();
unsigned long long iPow ();
};
我在gdb输出下面复制了this->值的值,this-> revValue,以及在iPow()循环的每次迭代中的this->结果:
Enter a number between 0 and 99999 (exlusive):
1234
Breakpoint 1, userinput::iPow (this=0x7fffffffde10) at program.cpp:49
49 while(this->revValue)
Missing separate debuginfos, use: debuginfo-install glibc-2.15-56.fc17.x86_64 libgcc-4.7.0-5.fc17.x86_64 libstdc++-4.7.0-5.fc17.x86_64
(gdb) p this->revValue
$1 = 4321
(gdb) n
51 if (this->revValue & 1ULL)
(gdb) p this->revValue & 1ULL
$2 = 1
(gdb) n
53 this->result *= this->value;
(gdb) n
55 this->revValue >>= 1;
(gdb) p this->result
$3 = 1234
(gdb) n
56 this->value *= this->value;
(gdb) p this->revValue
$4 = 2160
(gdb) n
49 while(this->revValue)
(gdb) p this->value
$5 = 1522756
(gdb) n
51 if (this->revValue & 1ULL)
(gdb) p this->revValue & 1ULL
$6 = 0
(gdb) n
55 this->revValue >>= 1;
(gdb) n
56 this->value *= this->value;
(gdb) p this->revValue
$7 = 1080
(gdb) n
49 while(this->revValue)
(gdb) p this->value
$8 = 2318785835536
(gdb) n
51 if (this->revValue & 1ULL)
(gdb) p this->revValue & 1ULL
$9 = 0
(gdb) n
55 this->revValue >>= 1;
(gdb) n
56 this->value *= this->value;
(gdb) p this->revValue
$10 = 540
(gdb) n
49 while(this->revValue)
(gdb) p this->value
$11 = 3022197894083133696
(gdb) n
51 if (this->revValue & 1ULL)
(gdb) p this->revValue & 1ULL
$12 = 0
(gdb) n
55 this->revValue >>= 1;
(gdb) n
56 this->value *= this->value;
(gdb) p this->revValue
$13 = 270
(gdb) n
49 while(this->revValue)
(gdb) p this->value
$14 = 16658586050838462464
(gdb) n
51 if (this->revValue & 1ULL)
(gdb) p this->revValue & 1ULL
$15 = 0
(gdb) n
55 this->revValue >>= 1;
(gdb) n
56 this->value *= this->value;
(gdb) p this->revValue
$16 = 135
(gdb) n
49 while(this->revValue)
(gdb) p this->value
$17 = 8477297326610710528
(gdb) n
51 if (this->revValue & 1ULL)
(gdb) p this->revValue & 1ULL
$18 = 1
(gdb) n
53 this->result *= this->value;
(gdb) n
55 this->revValue >>= 1;
(gdb) p this->result
$19 = 1681011244301025280
(gdb) n
56 this->value *= this->value;
(gdb) p this->revValue
$20 = 67
(gdb) n
49 while(this->revValue)
(gdb) p this->value
$21 = 0
(gdb) n
51 if (this->revValue & 1ULL)
(gdb) p this->revValue & 1ULL
$22 = 1
(gdb) n
53 this->result *= this->value;
(gdb) n
55 this->revValue >>= 1;
(gdb) p this->result
$23 = 0
(gdb) n
56 this->value *= this->value;
(gdb) p this->revValue
$24 = 33
(gdb) n
49 while(this->revValue)
(gdb) p this->value
$25 = 0
(gdb)
如gdb输出中所示,此>值获得的最大值为 $ 17 = 8477297326610710528 ,之后该值在下一次迭代中截断为零。因此,由于循环逻辑,this->结果的值也会截断为零。
是否存在可用于打印iPow()结果的所有数字的数据类型,考虑范围0-99999(独占)?或者,如果没有可行的数据类型,我可以用什么方法将iPow()的所有数字打印到stdout?
请分享您的想法。
TIA 维诺德
答案 0 :(得分:2)
你的计算中有溢出。 unsigned long long
在您的情况下有64位。 C ++规范提到无符号数的乘法使用模运算。也就是说,乘法的结果是正确的,直到2 64 的倍数。
在你的情况下:
1681011244301025280
是0x175426D200000000
(为方便起见,我转换为十六进制)
乘以它自己:
0x175426D200000000 * 0x175426D200000000 =
(0x175426D2 * 2^32) * (0x175426D2 * 2^32) =
(0x175426D2 * 0x175426D2) * (2^32 * 2^32) =
(whatever) * 2^64) ≡ 0 (modulo 2^64)
因此,当64位不足以保持结果时,为什么你的大数乘以它给出0是完全可以理解的。
要消除溢出的可能性,请使用bigint类型(bigint
是Internet搜索的关键字)。
bigint库的推荐问题是例如here