使用无符号long long时截断

时间:2015-08-31 09:45:22

标签: c++

这篇文章与我以前的帖子有关:

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 维诺德

1 个答案:

答案 0 :(得分:2)

你的计算中有溢出。 unsigned long long在您的情况下有64位。 C ++规范提到无符号数的乘法使用模运算。也就是说,乘法的结果是正确的,直到2 64 的倍数。

在你的情况下:

16810112443010252800x175426D200000000(为方便起见,我转换为十六进制)

乘以它自己:

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