【问题标题】:Acting like unsigned int overflow. What is causing it?表现得像 unsigned int 溢出。是什么原因造成的?
【发布时间】:2013-02-23 19:39:15
【问题描述】:

我有这个函数可以生成指定数量的所谓“三角形数”。如果我打印出双端队列后缀,数字会增加,向下跳,然后再次增加。随着 i 的上升,三角形的数字永远不会变小,所以一定会发生某种溢出。如果结果溢出,我尝试通过添加行 if(toPush > INT_MAX) return i - 1; 来尝试阻止函数生成更多数字(并返回它生成的数字)来修复它。但是,这不起作用,输出仍然不正确(增加一段时间,跳到较低的数字,然后再次增加)。我添加的行实际上似乎根本没有做任何事情。未达到退货。有谁知道这里发生了什么?

#include <iostream>
#include <deque>
#include <climits>
int generateTriangleNumbers(std::deque<unsigned int> &triangleNumbers, unsigned int generateCount) {
    for(unsigned int i = 1; i <= generateCount; i++) {
         unsigned int toPush = (i * (i + 1)) / 2;
         if(toPush > INT_MAX) return i - 1;
         triangleNumbers.push_back(toPush);
    }
return generateCount;
}

【问题讨论】:

  • 第一个不适用的 i 值是什么?如果您手动进行特定的计算,您会观察到什么?
  • 您如何期望任何数字严格大于最大可能数字?
  • 您应该检查计算的各个步骤。具体来说,如果溢出发生在(i * (i + 1)),你以后就无法检测到了。
  • @Mat 我认为作为测试我会检查它是否大于 INT_MAX 但仍使用无符号整数。这样,希望它会在数字增长到可能大于无符号整数的大小之前找出问题。
  • @OliCharlesworth 我将尝试弄清楚它发生在 i 的什么值

标签: c++ integer overflow limit


【解决方案1】:

在 Visual C++ 中,int(当然还有unsigned int)即使在 64 位计算机上也是 32 位。

使用 unsigned long longuint64_t 来使用 64 位值。

【讨论】:

    【解决方案2】:

    第 92682 个三角形数已经大于 UINT32_MAX。但这里的罪魁祸首要早得多,在i * (i + 1) 的计算中。在那里,第 65536 个三角数的计算溢出。如果我们询问 Python 的原生 bignum 支持:

    >>> 2**16 * (2**16+1) > 0xffffffff
    True
    

    哎呀。然后,如果您检查存储的数字,您将看到您的序列回落到低值。为了尝试模仿标准对这种情况的行为的描述,在 Python 中:

    >>> (int(2**16 * (2**16+1)) % 0xffffffff) >> 1
    32768
    

    这是您将看到的第 65536 个三角形数的值,这是不正确的。

    这里检测溢出的一种方法是确保生成的数字序列是单调的;也就是说,如果生成的第 N 个三角形数严格大于第 (N-1) 个三角形数。

    为避免溢出,您可以使用 64 位变量两者生成和存储它们,或者如果您需要大量三角形数,则使用大数库。

    【讨论】:

    • 这非常有效。我没想过简单地检查新数字是否大于上一个。我更改了代码,它现在在正确的时间退出。
    【解决方案3】:

    INT_MAXsigned int 的最大值。它大约是unsigned int (UINT_MAX) 最大值的一半。您对toPush 的计算可能会比UINT_MAX 高得多,因为您将值平方(如果它接近INT_MAX,则结果将远大于UINT_MAX 可以容纳的toPush)。在这种情况下,toPush 环绕并导致值小于前一个值。

    首先,您与INT_MAX 的比较是有缺陷的,因为您的类型是unsigned int,而不是signed int。其次,即使是与UINT_MAX 的比较也是不正确的,因为这意味着toPush(比较表达式的左操作数)可以保持一个高于其最大值的值——这是不可能的。正确的方法是将您生成的数字与前一个数字进行比较。如果它较低,您知道您有溢出,您应该停止。

    此外,您可能希望使用可以容纳更大范围值的类型(例如unsigned long long)。

    【讨论】:

      猜你喜欢
      • 2018-08-20
      • 1970-01-01
      • 2015-01-07
      • 2022-06-22
      • 2021-07-04
      • 2021-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多