【问题标题】:Why do I get errors when using unsigned integers in an expression with C++?为什么在 C++ 表达式中使用无符号整数时会出错?
【发布时间】:2010-05-17 17:04:19
【问题描述】:

给定以下一段(伪 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;
}

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

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

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

【问题讨论】:

  • 为什么要使用未签名的?它们真的只适用于模算术。使用它们来应用“这不应该成为负面”约束并不是一个很好的用途。

标签: c++ expression unsigned


【解决方案1】:

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

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

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

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

【讨论】:

  • 我必须不同意“使用无符号类型总是一个好主意”。我的银行帐户应该始终是肯定的,是的。但是使用无符号数量意味着如果出现问题,我最终会得到一个巨大的正余额。好的。也许银行应该在他们的非信用账户上使用未签名的数量:P
  • 重要的是实体是否可以在语义上变为否定。我银行的余额可以为负数,但电子表格中的行数不能。仅当它与您的类型的语义匹配时才使用 unsigned,并且仅当您愿意付出额外的努力以避免欠载时才使用。
  • 似乎有两种相反的观点:有些人建议使用无符号类型,有些人反对使用无符号类型,两者都有充分的理由。目前我转向后一个位置;与通过强制执行某些规则来帮助避免错误的严格类型控制、常量正确性和其他机制不同,无符号数据类型中没有任何内容可以防止分配(或结果)负值,这可能(默默地)导致灾难,恕我直言。
【解决方案2】:

如何制作heightwidthhw 签名整数?通过使它们未签名,您真正获得了什么?混合有符号和无符号整数总是自找麻烦。至少乍一看,在这里使用无符号值似乎没有任何收获。所以,你不妨让他们都签名,省去你自己的麻烦。

【讨论】:

  • 高度或宽度可以(语义)为负数吗?这导致了其他有趣的问题,例如负面积和体积。如果房子的面积是负数,一个人可以赚取税金吗?如果我的车有负长度,我的车能装进一个小的停车位吗?嗯.....
  • 语义并不总是问题。有很多时候,签名工作得很好,而且你能更好地找到错误,因为在由于错误而环绕时,你最终会得到负数而不是非常小的或非常大的正数。然而,在某些情况下,使用无符号值而不是有符号值确实很有意义。这取决于你在做什么。然而,重要的是不要混合有符号和无符号值。 才是真正麻烦的地方。尽管如此,我还是主张不要使用 unsigned ,除非你需要它,因为它更容易出错。
【解决方案3】:

您的转换规则是倒退的——当您混合相同类型的有符号和无符号版本时,有符号操作数将转换为无符号。

【讨论】:

    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 2019-06-05
        • 2015-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-22
        • 2019-02-06
        • 2011-07-16
        相关资源
        最近更新 更多