What Is Double Evaluation and Why Should It Be Avoided

What is double evaluation and why should it be avoided?

Imagine you wrote this:

#define Max(a,b) (a < b ? b : a)

int x(){ turnLeft(); return 0; }
int y(){ turnRight(); return 1; }

then called it like this:

auto var = Max(x(), y());

Do you know that turnRight() will be executed twice? That macro, Max will expand to:

auto var = (x() < y() ? y() : x());

After evaluating the condition x() < y(), the program then takes the required branch between y() : x(): in our case true, which calls y() for the second time. See it Live On Coliru.

Simply put, passing an expression as an argument to your function-like macro, Max will potentially evaluate that expression twice, because the expression will be repeated where ever the macro parameter it takes on, is used in the macro's definition. Remember, macros are handled by the preprocessor.


So, bottom line is, do not use macros to define a function (actually an expression in this case) simply because you want it to be generic, while it can be effectively done using a function templates

PS: C++ has a std::max template function.

Difference in evaluation of expression when using long long int vs double in c++

Some code to walk you through it, bottom line don't mix doubles with ints implicitly

#include <cassert>
#include <iostream>
#include <type_traits>

// typedef long long int ll; NO , use using and never use aliasing to safe a bit of typing. Aliases are there to introduce meaning not shortcuts

//using namespace std; // also NO

int main()
{
long long int lli_a = 603828039791327040;
long long int lli_b = 121645100408832000;
//double d_b = (double)lli_b; // No this is C++ don't use 'C' style casts
double d_b = static_cast<double>(lli_b);

assert(static_cast<long long int>(d_b) == lli_b); // you are in luck the double can represent your value exectly, NOT guaranteed

std::cout << "a " << lli_b - d_b << "\n"; // endl; \\0 don't use endl unless you have a good reason to flush

long long int lli_b4 = 4 * lli_b;

// use auto to show you this expression evaluates to a double!
auto lli_d_b4 = (lli_a - static_cast<long long int>(4) * d_b); // d_b is double!!! what do you want to do here? Use it as a long long int then cast it first
static_assert(std::is_same_v<double, decltype(lli_d_b4)>);

auto result_c = lli_b4 - lli_d_b4;
// result c is still a double!
static_assert(std::is_same_v<double, decltype(result_c)>);
std::cout << "c " << result_c << "\n";

// long story short don't mix types implicitly and use "C++" style cast explicitly to get the results you want

/*


cout << "b " << (lli_a - 4 * lli_b) - (lli_a - 4 * d_b) << endl; \\64
cout << "c " << (lli_a - 4 * lli_b) - (lli_a - (ll)4 * d_b) << endl; \\64
cout << "d " << (lli_a - 4 * lli_b) - (lli_a - 4 * (ll)d_b) << endl; \\0
cout << "e " << 4 * (ll)d_b - 4 * d_b << endl; \\0
cout << "f " << 4 * (ll)d_b - (ll)4 * d_b << endl; \\0
*/

return 0;
}

Can I remove double evaluation whilst keeping lambda expression

Preevaluate:

foo    = lambda(x): x if x > 10 else 0
result = foo(bar(x))

Here you can see what is the similarities to your code:

lambda (x): foo(x) if foo(x) > 10 else 0 == (lambda(x): x if x > 10 else 0)(foo(x))

What you seek is not possible unless you create some kind of mutable state object or some other weird trick.

@Alakazam answer is tricky and smart, use it on your own risk. It can be better using iterators and next to avoid extra intermediate list:

lambda x: next(res if res > 10 else 0 for res in (bar(x), ))

Here you have the live example

Double evaluation within macro: a case of sizeof() to determine array's size passed as compound literal

will f2() be invoked twice?

No, sizeof is not evaluated (unless it's a variable length array, but it's not).

what the standard says about that. Is there a guarantee ?

From C11 6.5.3.4p2:

The sizeof operator yields the size (in bytes) of its operand, [...] If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.

Is it implementation dependent ?

No, it should be always fine.

Note that your other macro uses (const float*)(__VA_ARGS__), that will not work - the syntax is (float[]){ stuff }. Anyway, I would just do one macro, why two, too much typing. Just:

#define SUMF_ARRAY(...)  \
sumf( \
(const float[]){__VA_ARGS__}, \
sizeof((const float[]){__VA_ARGS__}) / sizeof(float))
float total = SUMF_ARRAY(f1(), f2(), f3());

Why the global variable gets doubled twice?

#define abs(x) (x < 0 ? -x : x)

Macros perform textual substitution. Defining an abs() macro like this is a well-known trap because it ends up evaluating x twice. If it were a function it'd be fine, but as a macro it's a problem. abs(doubleGlobalX()) expands to:

doubleGlobalX() < 0 ? -doubleGlobalX() : doubleGlobalX()

It ends up calling doubleGlobalX() twice which causes x to be doubled twice.

When getting the max var, what's difference between using function and macro

Consider an usage like in this code sample:

#define max(a,b) (a>b?a:b)

int main()
{

int a = 0;
int b = 1;

int c = max(a++, b++);

cout << a << endl << b << endl;
return 0;

}

The intention probably was to print 1 and 2, but macro expands to:

int c = a++ > b++ ? a++ : b++;

b gets incremented twice, and the program prints 1 and 3.

Hence,

In some cases, expressions passed as arguments to macros can be evaluated more than once.

Struts 2 - The usage of %{ } notation

Used in OGNL to force evaluate the content in brackets as OGNL expression. For example

<s:iterator value = "myAnswers" status="stat">
<s:textfield name = "myAnswers[%{#stat.index}].name"/>

evaluation of the stat.index should be forced.

Evaluation order of double assignment

Assignments associate right-to-left. In other words:

a = b = c;

is parsed as

a = (b = c);

and this order is guaranteed by the standard.

That is, for primitive types, it will assign c into both b and a. For non-primitive types, it will call a.operator= (b.operator= (c)).

SQLITE Case Expression with variable to avoid double evaluation of expression

You can evaluate expression in subquery and then use it's name (expr) in query:

SELECT CASE
WHEN expr > 0 THEN expr
ELSE 0
END
FROM (
SELECT
-- add BUCKETS.*, here if you need other BUCKETS fields return to query
CAST(ROUND(VOLUME - ((CAPACITY*1.0)/TIMEFRAME)*(STRFTIME('%s','now') - UPDATED_TIMESTAMP)) AS INT) AS expr
FROM BUCKETS
) b


Related Topics



Leave a reply



Submit