【问题标题】:Casting in a compound assignment operator?铸造复合赋值运算符?
【发布时间】:2012-06-06 14:45:06
【问题描述】:

我的代码简化为:

int x = 5;
x *= 0.5;

使用 Visual Studio 进行编译时,我收到一条警告 C4244,提示可能丢失数据 - 当然,因为(简化)将 int 乘以 double 会导致 double,然后将其转换为 int,从而丢失非-整数部分。

我的问题是,是否有 C++ 语法来指示这是预期的行为,以便使警告静音(我知道 pragma push/pop 以禁用警告,我认为更清楚地表明这实际上是预期的行为,而且我不只是压制警告)。

长格式是在乘法之后显式转换,如下所示:

x = (int)(x * 0.5);

但复合运算符符号更易于阅读。

那么,有没有办法像这样投射?我已经尝试将“(int)”放在我能想到的每个位置,但它们似乎都不是有效的 C++ :(

【问题讨论】:

  • 我很确定会消除您需要切换到其他内容的警告(例如,x /= 2;x >>= 1;)。
  • 总是0.5?如果您对小数部分不感兴趣,为什么不使用整数除法?
  • 顺便说一下,C++ 方式是x = static_cast<int>(x * 0.5)。在 C++ 中不鼓励使用括号强制转换。
  • @JerryCoffin:我不会使用右移(OP 使用有符号类型,如果 x < 0 行为变为实现定义)。
  • 编译器将在适当的地方使用移位。只写最自然的东西(即x /= 2;表示除法)。

标签: c++


【解决方案1】:

不是乘以 0.5,而是除以 2。两个 ints 将使用整数除法,不会出现警告。

如果你真的想乘以浮点数/双精度数,请不要使用int 来存储结果。

【讨论】:

  • 使用常量除数,任何体面的优化器都不应该使用整数除法。
  • 我并不总是乘以 0.5,这只是一个例子,乘法可以是任何双精度值。它存储在 int 中,因为这是我正在使用的第三方库中的类中成员的类型。我很感激你花时间发帖,毫无疑问是为了提供帮助,但是在 Stackoverflow 上我需要拼出我已经想到但不想使用的每一个反对意见或解决方法(无论原因)在一个问题中,仍然有人认为我是智障并且我没有意识到 * 0.5 == / 2。
  • @Roel:猜测 OP 这次可能会有的每一个愚蠢的限制也很累人。在帖子中包含所有相关信息。如果你真的想使用int,显式转换是唯一的选择。
  • @BenVoigt:我的意思是语义上,而不是微观性能。
  • @Roel 所以,你宁愿浪费别人的时间猜测你的要求?
【解决方案2】:

警告已到位 - 应该警告您可能会丢失数据。

任何避免警告的方法都涉及使用清楚表达您意图的语法。这可以通过以下方式完成:

  1. 创建一个满足您需求的函数。
  2. 不要使用一元运算符。相反,使用二元运算符,并显式截断结果:x = static_cast<int>(x * 0.5);

使用一元运算符*,您无法轻松取消警告,因为该运算符旨在以清晰直接的方式成为用户,不涉及强制转换 - 而您的案例不满足此要求。所以你不能这样做是一件的事情。

【讨论】:

  • +1 表示“创建一个执行您想要的功能”。然后可以隐藏丑陋的演员表以及任何舍入逻辑,调用代码只会看到一个简短的描述性名称,例如rescalemultiply_by
【解决方案3】:

根据 OP 的 cmets,可能最好建议您使用来自 <cmath> 的适当函数,具体取决于您想要的 舍入行为 类型(有偏/无偏)。

例如,floor 通常会进行有偏舍入(偏向负无穷大,因为它总是选择较小的整数),即floor( -7.5 ) 会给你-8,如果你愿意,你需要自己滚动对称舍入,即希望 floor( -7.5 ) 成为 -7(如 floor( 7.5 ) 给出 7)。

round 但是可以调整以产生无偏的舍入。通常,它将从round( 10.3 ) 生成10,从round( 10.6 )(或更大)生成11。在中间(tie-breaking)舍入仍然存在问题,即round( 10.5 ) 应该提供什么(它通常会产生11)。如果这种行为不适合您,您可能需要查看一些替代方法,例如 Banker's Rounding/Alternate Rounding 等。

【讨论】:

    【解决方案4】:

    int 和 float 相乘的结果是 float

    如果要将 int 转换为浮点数,则可能需要对浮点数进行四舍五入以去除十进制值。如果你只是简单地转换它,你可能会出现舍入错误。

    例如这个函数会进行四舍五入(还有其他的舍入方式):

    int round(float x)
    {
      return static_cast<int>((x > 0.5) ? ceil(x) : floor(x));
    } 
    

    那么你只需要这样称呼它:

      int x = 5;
      float y = 0.33f;
      x = round(x * y);
    

    并且您没有警告,并且 float 已正确舍入并转换为 int

    对于 *= 运算符,我没有一个合适的解决方案,它具有良好的舍入,因此使用此解决方案,您将不得不替换 *=

    【讨论】:

      猜你喜欢
      • 2011-11-16
      • 2015-10-15
      • 2020-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-17
      • 1970-01-01
      相关资源
      最近更新 更多