【问题标题】:Interger underflow causing modulo to fail整数下溢导致模数失败
【发布时间】:2019-08-12 04:53:56
【问题描述】:

我有 4 张图像要显示,所以我使用 unsigned short 来表示当前图像的索引。当我按 A 时,索引减 1;当我按 D 时,索引增加 1。

我正在使用 image_index = (image_index - 1) % 4; 计算 A 按键上的索引(在 D 按键上,image_index = (image_index + 1) % 4;

如果我向前循环(IE,按 D),一切都会按预期工作,但如果我在索引 0 并按 A,它会下溢到无符号短整数的最大值,并且不会以 4 为模给我索引 3。

我知道对于有符号类型,上溢/下溢是 UB,但我确信对于无符号类型,它是明确定义的。有什么想法可能导致它忽略模数?

enum class Errors : short {
  kSdlSuccess = 0,
  kSdlInitFailure = -1,
  kSdlWindowCreationError = -2,
  kSdlRendererCreationError = -3
};

int main(int argc, char** argv) {

  sdl::Context ctx;
  sdl::Window window({ .w = 600, .h = 480, .flags = 0});
  sdl::Renderer renderer(window, {0});

  bool is_running {true};
  unsigned short texture_index = 0;
  SDL_Event event {};
  while(is_running) {
    while(SDL_PollEvent(&event)) {
      if(event.type == SDL_QUIT) { is_running = false; }
      else if(event.type == SDL_KEYDOWN) {
        if(event.key.keysym.scancode == SDL_SCANCODE_A) { texture_index = (texture_index - 1) % 4; }
        else if(event.key.keysym.scancode == SDL_SCANCODE_D) { texture_index = (texture_index + 1) % 4; }
      }
    }

    printf("%d\n", texture_index);

    renderer.setDrawColor(255, 0, 0, 255);
    renderer.clearBuffer();
    renderer.setDrawColor(0,0,0,255);
    renderer.drawTexture(texture_index);
    renderer.swapBuffer();
  }
  return static_cast<int>(Errors::kSdlSuccess);
}

【问题讨论】:

  • 试试texture_index = (texture_index - 1u) % 4u;
  • 没有整数下溢之类的东西。对于有符号整数,超过任一限制称为溢出。对于无符号整数没有溢出,算术定义为模 (max+1)。无论如何,根据积分提升规则, unsigned short 被提升为 signed int。
  • 当然还有 (x-1)%4 == (x+3)%4 所以...

标签: c++ undefined-behavior modulo underflow


【解决方案1】:

问题:

这是由于将unsigned short 类型提升为(signed) int。 (详情请参阅this answer)。

假设您的 texture_index 最初为零。

当您执行texture_index - 1 时,您的texture_index 将提升为(signed) int。这种计算的结果是-1 in (signed) int(-1) % 4 的结果是 -1(涉及负值的模计算可能很棘手且违反直觉,请参阅 this question 了解更多详细信息)。

然后,您将-1signed int)分配(转换)为texture_indexunsigned short)。这种转换产生65535(或0xFFFF)。有关有符号到无符号转换的更多详细信息,请参阅this answer。所以问题不在于忽略的模运算,而在于不需要的类型转换(或提升)。


解决办法:

因此解决方案是消除不必要的转换。

在这个问题下的一个 cmets 中,我看到了这样的内容:

texture_index = (texture_index - 1u) % 4u;

这消除了对signed int 的转换,太好了。它仍然会触发对unsigned int 的提升(因为1u4uunsigned int 文字),但由于您的模数很小,这并不重要。

这在您的情况下可以正常工作,但很脆弱

如果有一天你想要张图片怎么办?

unsigned short texture_index = 0;
texture_index = (texture_index - 1U) % 5U;
assert(texture_index == 4U);    //Assertion fails!

为什么?调试器现在说texture_index0。那是因为0U - 1U == 4294967295U

绕过此类问题的一个好技巧是在进行取模之前将除数添加到被除数中。

unsigned short texture_index = 0;
texture_index = (texture_index - 1U + 5U) % 5U;   //Add 5U before modding 5U
assert(texture_index == 4U); //Assertion passes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-28
    • 1970-01-01
    • 2011-06-12
    • 2011-12-07
    • 1970-01-01
    相关资源
    最近更新 更多