2

给定以下一段(伪 C++)代码:

float x=100, a=0.1;
unsigned int height = 63, width = 63;
unsigned int hw=31;
for (int row=0; row < height; ++row)
{
    for (int col=0; col < width; ++col)
    {
        float foo = x + col - hw + a * (col - hw);
        cout << foo << " ";
    }
    cout << endl;
}

在 (col - hw) 为负的地方, foo 的值在数组的一半处被搞砸了。我想因为 col 是 int 并且首先出现,所以表达式的这一部分被转换为 int 并变为负数。不幸的是,显然它没有,我得到一个无符号值的溢出,我不知道为什么。

我应该如何解决这个问题?对整个或部分表达式使用强制转换?什么类型的转换(C 风格或 static_cast<...>)?使用演员表是否有任何开销(我需要它快速工作!)?

编辑:我将所有无符号整数都更改为常规整数,但我仍然想知道为什么在这种情况下会溢出。

4

5 回答 5

7

无符号整数实现无符号算术。无符号算术是模算术。所有值都以 2^N 为模进行调整,其中 N 是无符号类型的值表示中的位数。

简单来说,无符号算术总是产生非负值。每次表达式应该产生负值时,该值实际上“环绕” 2^N 并变为正值。

当您在 [sub-] 表达式中混合有符号和无符号整数时,无符号算术“获胜”,即在无符号域中执行计算。例如,当您执行 时col - hw,它被解释为(unsigned) col - hw。这意味着 forcol == 0hs == 31你不会得到-31结果。相反,您会得到UINT_MAX - 31 + 1,这通常是一个巨大的正值。

话虽如此,我必须指出,在我看来,使用无符号类型来表示固有的非负值总是一个好主意。事实上,在实践中,C/C++ 中的大多数(或至少一半)整数变量都应该具有无符号类型。您在示例中使用无符号类型的尝试是有充分理由的(如果正确理解意图)。此外,我会使用unsignedforcolrow也是。但是,您必须记住无符号算术的工作方式(如上所述)并相应地编写表达式。大多数时候,一个表达式可以被重写,这样它就不会越过无符号范围的边界,也就是说,大多数时候不需要显式地将任何东西转换为有符号类型。否则,如果您最终确实需要使用负值,则适当地转换为有符号类型应该可以解决问题。

于 2010-05-17T17:18:24.737 回答
2

如何制作height,widthhw签名的整数?通过使它们未签名,您真正获得了什么?混合有符号和无符号整数总是自找麻烦。至少乍一看,在这里使用无符号值似乎没有任何收获。因此,您不妨将它们全部签名并省去麻烦。

于 2010-05-17T17:09:07.727 回答
1

你有向后的转换规则——当你混合相同类型的有符号和无符号版本时,有符号的操作数被转换为无符号的。

于 2010-05-17T17:19:09.090 回答
0

如果您希望这更快,您应该在开始循环之前 static_cast 所有无符号值并使用它们的 int 版本而不是 unsigned int。您仍然可以要求输入未签名,然后将它们投射到您的算法中,以保留您的函数所需的域。

于 2010-05-17T17:10:48.920 回答
0

强制转换不会自动发生 - 未强制转换的算术仍然有它的用途。通常的示例是 int / int = int,即使数据因不转换为浮点数而丢失。我会使用signed int,除非因为INT_MAX太小而无法这样做。

于 2010-05-17T17:20:49.790 回答