简体   繁体   English

C ++:评估顺序运算符时忽略括号

[英]C++ : parentheses ignored when evaluating sequential operators

First of all, I must say that the problem raised here is solved for me, and I wonder : 首先,我必须说这里提出的问题已经为我解决了,我想知道:

  • what I have misunderstood, 我误会了什么
  • if the compiler has an error (I know this is rare) (it is gcc 4.8.4). 如果编译器有错误(我知道这很罕见)(它是gcc 4.8.4)。

I want to calculate the norm of a 2-dimentional vector, of which coordinates are computed only at that moment. 我想计算一个二维向量的范数,该向量仅在那一刻才被计算。 Let's say, I want to calculate || 假设我要计算|| (x0, y0) - (x1, y1) || (x0,y0)-(x1,y1)|| , by the formula sqrt((x0-x1)**2 + (y0-y1)**2) . ,由公式sqrt((x0-x1)** 2 +(y0-y1)** 2)组成。 Only the result needs to be saved. 只需要保存结果。

For performance reasons, the square is done by self-multiplication, and I want the substractions and access to variables to be done only once. 出于性能原因,平方是通过自乘完成的,我希望减法和对变量的访问只能进行一次。 I want the total to be efficient at runtime, and somehow elegantly coded. 我希望总代码在运行时高效,并以某种方式优雅地编码。 I have thought three possibilities : 我想过三种可能性:

  • repeat twice x0 - x1 and y0 - y1 , and hope the optimization step of the compilator will detect the repetition, 重复两次x0 - x1y0 - y1 ,并希望编译器的优化步骤可以检测到重复,
  • use an inline function, 使用内联函数
  • use together a buffer variable and the sequential operator. 一起使用缓冲区变量和顺序运算符。

I decided to try the last option. 我决定尝试最后一种选择。


Consider now the following code : 现在考虑以下代码:

#include <cmath>
#include <stdio.h>

int main (void)
{
    float x0 (-1), x1 (2), y0 (13), y1 (9), result, tmp;

    result = std::sqrt ((tmp = x0 - x1, tmp * tmp) + (tmp = y0 - y1, tmp * tmp));
    printf ("%f\n", result);
}

I know I must obtain 5.000000 , but I obtain 5.656854 , which is sqrt((y0-y1)**2 + ((y0-y1)**2)). 我知道我必须获得5.000000 ,但我获得5.656854 ,这是sqrt((y0-y1)** 2 +((y0-y1)** 2))。

I can get the wanted result with : 我可以得到想要的结果:

#include <cmath>
#include <stdio.h>

int main (void)
{
    float x0 (-1), x1 (2), y0 (13), y1 (9), result, tmp, tmp2;

    result = std::sqrt ((tmp = x0 - x1, tmp * tmp) + (tmp2 = y0 - y1, tmp2 * tmp2));
    printf ("%f\n", result);
}

It is like the first parts of the sequential operators are evaluated at first, ignoring the parentheses and the return value of the first sequential operator. 就像首先对顺序运算符的第一部分进行求值,而忽略了第一个顺序运算符的括号和返回值。 Seems a bit akward ; 似乎有点尴尬; is there something in the definition of C++ that I missed here ? 我在这里错过了C ++的定义吗?

NB : Setting the optimization on or off changes nothing during the test. 注意:在测试过程中,打开或关闭优化不会改变任何内容。

Both sides of the operator + may be evaluated interleaved. operator +两侧都可以交错评估。 In particular, each of the two assignments in the parenthesis must happen before the multiplication to its immediate right (in correct code that is, which your first variant is not), but need not happen before the other assignment. 特别是,括号中的两个赋值中的每一个都必须发生在其直接右边的乘法之前(以正确的代码,即您的第一个变体不是),而不必发生在另一个赋值之前。 Also, the other assignment is allowed to happen in between one assignment and the matching multiplication. 另外,允许另一分配发生在一个分配与匹配乘法之间。

Your first variant thus invokes undefined behavior because it contains two unsequenced modification of tmp . 因此,您的第一个变体会调用未定义的行为,因为它包含tmp两个未排序的修改。 So literally every result is legal, including a crash or NaN. 因此,实际上每个结果都是合法的,包括崩溃或NaN。

In general, please keep in mind that "your code is clever" is not a compliment. 总的来说,请记住“您的代码很聪明”不是夸奖。 Keep it simple, if you really must optimize away the subtractions (you most likely do not): 保持简单,如果您真的必须优化减法(您很可能没有这样做):

auto dx = x0 - x1;
auto dy = y0 - y1;
auto result = std::sqrt(dx * dx + dy * dy);

This line: 这行:

result = std::sqrt ((tmp = x0 - x1, tmp * tmp) + (tmp = y0 - y1, tmp * tmp));

You should avoid modifying a value that you use elsewhere in the same expression. 您应该避免修改在同一表达式中其他地方使用的值。 Most operators are not guaranteed to evaluate their operands in any particular order (the exceptions are &&, ||, ?:, and the comma operator). 不能保证大多数运算符都可以按任何特定顺序评估其操作数(&&,||,?:和逗号运算符除外)。 In this case, the operands of the + operator in this expression could be evaluated in any order and not necessarily all at once, so your code has undefined behavior. 在这种情况下,此表达式中+运算符的操作数可以按任何顺序求值,而不必一次求值,因此您的代码具有未定义的行为。

Furthermore, unless you've profiled your code and know that you need a particular statement to be tightly optimized, you should prefer clarity over clever tricks. 此外,除非您已对代码进行了概要分析,并且知道需要对特定的语句进行严格的优化,否则您应该更喜欢清晰度而不是巧妙的技巧。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM