• Re: is "x *= ++f * ++f" a valid statement ? PLO

    From Richard Damon@21:1/5 to olcott on Fri Jun 21 18:10:50 2024
    On 6/21/24 5:55 PM, olcott wrote:
    On 6/18/2024 8:53 PM, Andrey Tarasevich wrote:
    On 05/23/24 7:14 AM, Bonita Montero wrote:
    Is "x *= ++f * ++f" a valid statement ?
    Or is there implementation defined behaviour ?

    The question is meaningless without knowing the types of objects
    involved. It has no specific answer


    If it works correctly for int then it is
    syntactically correct:
    int x = 1;
    int f = 1;
    x = 1 * (2 * 3);

    ++f could be defined as exit(0) for some UDT.


    But, the DEFINITON of ++ doesn't requring that one of the f's are
    incremented first, then you use the value, then the othero one. (unless
    the rules were actually changed).

    x *= ++f + ++f

    could be implemented as

    f = f+1;
    f = f+1;

    x *= f * f;

    it could even be done as
    ;
    tf1 = f+1
    tf2 = f+1;

    x *= tf1 + tf2;

    f = tf1;
    f = tf2;

    so, we have no promise that f even increases by 2


    At least this is what it could be under the original rules. I would have
    to see if it was tightened when threading was allowed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to olcott on Fri Jun 21 20:14:08 2024
    On 6/21/24 7:06 PM, olcott wrote:
    On 6/21/2024 5:10 PM, Richard Damon wrote:
    On 6/21/24 5:55 PM, olcott wrote:
    On 6/18/2024 8:53 PM, Andrey Tarasevich wrote:
    On 05/23/24 7:14 AM, Bonita Montero wrote:
    Is "x *= ++f * ++f" a valid statement ?
    Or is there implementation defined behaviour ?

    The question is meaningless without knowing the types of objects
    involved. It has no specific answer


    If it works correctly for int then it is
    syntactically correct:
    int x = 1;
    int f = 1;
    x = 1 * (2 * 3);

    ++f could be defined as exit(0) for some UDT.


    But, the DEFINITON of ++ doesn't requring that one of the f's are
    incremented first, then you use the value, then the othero one.
    (unless the rules were actually changed).


    When we assume the *= assigns
    the result of the RHS * the LHS to the LHS
    "x *= ++f * ++f"

    means x = x * (++f * ++f)
    thus cannot have implementation defined behavior.



    Sure it can, because the order of the parts of the operation of the two
    ++f is unspecified. In fact, the implementation doesn't need to define
    it at all, it is just unspecified (and it used to invoke Undefined
    Behavior, I am not sure if that clause is still there)

    Detail explaination of the operation of ++f has effectively 4 steps

    1) read the value of f
    2) increment the value read
    3) write that value back into f
    4) use that value in the expression

    Sequence wise, we have that 1 is before 2 is before 3, and 2 is before
    4, but 3 and 4 are not ordered with respect to one another.

    And, the sequence of operations between the two different ++f operations
    have no sequeenc between each other, only that both of their step 4 are
    working at the same time to make the final result of that operation.

    Thus, both reads of the value might occur before either write backs of
    either updated value, and thus f is only increased by 1 by the statement.

    In fact, the optimizer could well see that option and just read the
    value once increment it once, and write it back just once.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to olcott on Fri Jun 21 23:42:18 2024
    On 6/21/24 7:06 PM, olcott wrote:
    ...
    When we assume the *= assigns
    the result of the RHS * the LHS to the LHS
    "x *= ++f * ++f"

    means x = x * (++f * ++f)
    thus cannot have implementation defined behavior.

    You are correct about that conclusion, though I don't see what your
    premise has to do with it. The expression ++f * ++f has, all by itself, undefined behavior, independent of what larger expression it might be
    part of. "implementation-defined behavior" is defined by the standard as "behavior, for a well-formed program construct and correct data, that
    depends on the implementation and that each implementation documents"
    (3.13). Since this program is not well formed, implementations have no obligation to document what it's behavior will be, so it cannot be implementation-defined behavior.

    Repeating what I said in an earlier message (with one minor correction):

    "Except where noted, evaluations of operands of individual operators and
    of subexpressions of individual expressions are unsequenced." (6.9.1p10)

    Both ++f expressions are sub-expressions of the multiplication
    expression. Thus, the executions are unsequenced, which is not, in
    itself, a problem. However, they both have a side effect on the same
    memory location, and that is a problem, because that same clause goes on
    to say:

    "If a side effect on a memory location (6.7.1) is unsequenced relative
    to either another side effect on the same memory location or a value computation using the value of any object in the same memory location,
    and they are not potentially concurrent (6.9.2), the behavior is undefined."

    These two expressions are not potentially concurrent: they are in the
    same expression, so they must be in the same thread, and if they are in
    a signal handler, they must both be in the same signal handler.
    Therefore, the expression ++f * ++f has undefined behavior.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to olcott on Sat Jun 22 13:09:50 2024
    On 22/06/2024 06:02, olcott wrote:
    On 6/21/2024 10:42 PM, James Kuyper wrote:
    On 6/21/24 7:06 PM, olcott wrote:
    ...
    When we assume the *= assigns
    the result of the RHS * the LHS to the LHS
    "x *= ++f * ++f"

    means x = x * (++f * ++f)
    thus cannot have implementation defined behavior.

    You are correct about that conclusion, though I don't see what your
    premise has to do with it. The expression ++f * ++f has, all by itself,
    undefined behavior,

    It would seem to be naturally defined to be Left to right
    ++f from 2 to 3
    ++f from 3 to 4
    3 * 4


    Some programming languages define the order of evaluation for
    subexpressions like this. Neither C nor C++ do (except for specific
    operators, which do not include multiplication).

    It doesn't really matter what you consider "natural" or not - it matters
    what the standards say.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to olcott on Sat Jun 22 09:56:45 2024
    On 6/22/24 8:40 AM, olcott wrote:
    On 6/22/2024 6:09 AM, David Brown wrote:
    On 22/06/2024 06:02, olcott wrote:
    On 6/21/2024 10:42 PM, James Kuyper wrote:
    On 6/21/24 7:06 PM, olcott wrote:
    ...
    When we assume the *= assigns
    the result of the RHS * the LHS to the LHS
    "x *= ++f * ++f"

    means x = x * (++f * ++f)
    thus cannot have implementation defined behavior.

    You are correct about that conclusion, though I don't see what your
    premise has to do with it. The expression ++f * ++f has, all by itself, >>>> undefined behavior,

    It would seem to be naturally defined to be Left to right
    ++f from 2 to 3
    ++f from 3 to 4
    3 * 4


    Some programming languages define the order of evaluation for
    subexpressions like this.  Neither C nor C++ do (except for specific
    operators, which do not include multiplication).

    It doesn't really matter what you consider "natural" or not - it
    matters what the standards say.


    It would be bad for the standards to be counter-intuitive.


    No, the Standards are to define the "contract" between the programmer
    and the implementations.

    C (and to a lesser extent C++) were envisioned as "High Performance"
    languages designed to get efficiencies of code close to what could be
    created with pure assembly language. Leaving things unspecified that
    still allow the program to be ABLE to express what he wants, and also
    allowing the compiler to generate the most efficient code possible was
    the goal.

    Forcing a left-to-right (or right-to-left) order for expressions adds
    possible cost to the code generation due to possible need for spilling registers to temporary locations to compute other results.

    If you NEED the specific ordering, you can always pre-compute the sub-expressions to explicit temporaries, so forcing the order isn't that useful\.

    One of the main design ideas was that the program is assumed to be
    competent and knows the language well. (This may be less true for C++)
    and gives the programmer powerful tools that do allow them to shoot
    themselves in the foot.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to olcott on Sat Jun 22 09:45:24 2024
    On 6/22/24 8:38 AM, olcott wrote:
    On 6/22/2024 1:18 AM, Keith Thompson wrote:
    olcott <[email protected]> writes:
    [...]
    That is weird I wold have chosen left to right sequence.
    I thought that the order of arithmetic operations specifies
    left to right sequence.

    You may well have thought that.  You were wrong.  Do you understand that >> now?


    "x *= ++f * ++f"
    int x = 5;
    int y = 3;

    For the calculation is question is seems to make no difference to the
    result.
    x = 5 * (4 * 5)
    x = 5 * (5 * 4)



    In this case no, but if the operation was - it would,

    And, as pointed out there is no requirement on the ordering of even the sub-parts except determinism (we can't use a value we haven't computed
    yet). This means the ++ can be interleaved.

    And, because of the EXPLICIT requirement on updates to a value needing
    to be ordered to avoid undefined behavior, the compiler, seeing that
    Undefined Behavior exist there can do ANYTHING it wants with that code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to olcott on Sat Jun 22 10:23:35 2024
    On 6/22/24 10:13 AM, olcott wrote:
    On 6/22/2024 8:45 AM, Richard Damon wrote:
    On 6/22/24 8:38 AM, olcott wrote:
    On 6/22/2024 1:18 AM, Keith Thompson wrote:
    olcott <[email protected]> writes:
    [...]
    That is weird I wold have chosen left to right sequence.
    I thought that the order of arithmetic operations specifies
    left to right sequence.

    You may well have thought that.  You were wrong.  Do you understand
    that
    now?


    "x *= ++f * ++f"
    int x = 5;
    int y = 3;

    For the calculation is question is seems to make no difference to the
    result.
    x = 5 * (4 * 5)
    x = 5 * (5 * 4)



    In this case no, but if the operation was - it would,

    And, as pointed out there is no requirement on the ordering of even
    the sub-parts except determinism (we can't use a value we haven't
    computed yet). This means the ++ can be interleaved.

    And, because of the EXPLICIT requirement on updates to a value needing
    to be ordered to avoid undefined behavior,

    In other words you fail to comprehend that: (5 * 4) == (4 * 5)

    no, the issue is that the two ++f are unsequenced, so one possible
    result (ignore the allowance of total undefined behavior) is (4*4), sine
    the code COULD be compiled to the equivalent of:

    int __t1 = f;
    int __t2 = __t1+1;
    int __t3 = f;
    int __t4 = __t3+1;
    x *= __t2 * __t4;
    f = __t2;
    f = __t4;


    the compiler, seeing that Undefined Behavior exist there can do
    ANYTHING it wants with that code.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to olcott on Sat Jun 22 16:52:47 2024
    On 22/06/2024 14:40, olcott wrote:
    On 6/22/2024 6:09 AM, David Brown wrote:
    On 22/06/2024 06:02, olcott wrote:
    On 6/21/2024 10:42 PM, James Kuyper wrote:
    On 6/21/24 7:06 PM, olcott wrote:
    ...
    When we assume the *= assigns
    the result of the RHS * the LHS to the LHS
    "x *= ++f * ++f"

    means x = x * (++f * ++f)
    thus cannot have implementation defined behavior.

    You are correct about that conclusion, though I don't see what your
    premise has to do with it. The expression ++f * ++f has, all by itself, >>>> undefined behavior,

    It would seem to be naturally defined to be Left to right
    ++f from 2 to 3
    ++f from 3 to 4
    3 * 4


    Some programming languages define the order of evaluation for
    subexpressions like this.  Neither C nor C++ do (except for specific
    operators, which do not include multiplication).

    It doesn't really matter what you consider "natural" or not - it
    matters what the standards say.


    It would be bad for the standards to be counter-intuitive.


    People's "intuition" varies wildly. Pretty much any definition would be counter-intuitive to someone. That's why language standards try to have precise definitions, rather than just saying "it all works pretty much
    like you'd expect".

    We realise the definition of C and C++ expression evaluation does not
    match what you thought was "intuitive". That does not matter - it would
    not matter even if lots of C and C++ programmers agreed with you. What
    matters for these languages is what their standards /say/. That way, we
    can look at clear facts written in official documents, rather than
    trying to rely on what random people think about to be how they imagine
    things ought to be.

    You can happily feel that C would be better if evaluation order were
    strictly define. Some people will agree with that, others will disagree
    - but it will not make a blind bit of difference to the actual /facts/
    of the matter.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to olcott on Sat Jun 22 13:20:42 2024
    On 6/22/24 12:27 PM, olcott wrote:
    On 6/22/2024 9:52 AM, David Brown wrote:
    On 22/06/2024 14:40, olcott wrote:
    On 6/22/2024 6:09 AM, David Brown wrote:
    On 22/06/2024 06:02, olcott wrote:
    On 6/21/2024 10:42 PM, James Kuyper wrote:
    On 6/21/24 7:06 PM, olcott wrote:
    ...
    When we assume the *= assigns
    the result of the RHS * the LHS to the LHS
    "x *= ++f * ++f"

    means x = x * (++f * ++f)
    thus cannot have implementation defined behavior.

    You are correct about that conclusion, though I don't see what your >>>>>> premise has to do with it. The expression ++f * ++f has, all by
    itself,
    undefined behavior,

    It would seem to be naturally defined to be Left to right
    ++f from 2 to 3
    ++f from 3 to 4
    3 * 4


    Some programming languages define the order of evaluation for
    subexpressions like this.  Neither C nor C++ do (except for specific
    operators, which do not include multiplication).

    It doesn't really matter what you consider "natural" or not - it
    matters what the standards say.


    It would be bad for the standards to be counter-intuitive.


    People's "intuition" varies wildly.  Pretty much any definition would
    be counter-intuitive to someone.  That's why language standards try to
    have precise definitions, rather than just saying "it all works pretty
    much like you'd expect".

    We realise the definition of C and C++ expression evaluation does not
    match what you thought was "intuitive".  That does not matter - it
    would not matter even if lots of C and C++ programmers agreed with
    you.  What matters for these languages is what their standards /say/.
    That way, we can look at clear facts written in official documents,
    rather than trying to rely on what random people think about to be how
    they imagine things ought to be.

    You can happily feel that C would be better if evaluation order were
    strictly define.  Some people will agree with that, others will
    disagree - but it will not make a blind bit of difference to the
    actual /facts/ of the matter.



    Then make the rule strict left to right unless otherwise specified.
    int x = 2 + 3 * 5; // is otherwise specified by operator precedence.


    Join the ISO Committe and try to lobby for it.

    There are good reasons it isn't defined that way.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to olcott on Sat Jun 22 15:48:43 2024
    On 22/06/2024 06:02, olcott wrote:
    On 6/21/2024 10:42 PM, James Kuyper wrote:
    On 6/21/24 7:06 PM, olcott wrote:
    ...
    When we assume the *= assigns
    the result of the RHS * the LHS to the LHS
    "x *= ++f * ++f"

    means x = x * (++f * ++f)
    thus cannot have implementation defined behavior.

    You are correct about that conclusion, though I don't see what your
    premise has to do with it. The expression ++f * ++f has, all by itself,
    undefined behavior,

    It would seem to be naturally defined to be Left to right
    ++f from 2 to 3
    ++f from 3 to 4
    3 * 4

    C is deliberately underspecified, to allow implementations more freedom
    to optimize their code. The key point is that ++f * ++f modifies the
    same object twice without any requirement that the updates occur in a particular order. This means an implementation is allowed to generate
    code that implements those modifications in such a way that they could interfere with each other.

    Consider, for example, an implementation where f is a 64-bit long long
    on a system with no hardware support for types longer than 16 bits, so
    that it needs to generate multiple machine instructions to implement
    ++f. If you were calculating ++f * ++g, it would be entirely feasible to interleave the instructions that calculate ++f and ++g; there might, in
    fact, be some advantage to doing so. When the implementor writes the
    code that implements that interleaving, the fact that ++f * ++f would
    have undefined behavior relieves the implementor of any responsibility
    for checking whether the two ++ operations apply to the same object,
    which simplifies the logic of that code.

    I'm not saying that these rules were chosen with this particular example
    in mind, but only that examples like this one were the reason why C
    takes the general approach that it does: unless otherwise specified,
    operations on sub-expressions are unordered with respect to each other,
    and when an action modifying an object is unordered with respect to
    another operation that either reads or modifies that object, the
    behavior is undefined.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mikko@21:1/5 to Richard Damon on Sun Jun 23 11:48:54 2024
    On 2024-06-22 13:45:24 +0000, Richard Damon said:

    On 6/22/24 8:38 AM, olcott wrote:
    On 6/22/2024 1:18 AM, Keith Thompson wrote:
    olcott <[email protected]> writes:
    [...]
    That is weird I wold have chosen left to right sequence.
    I thought that the order of arithmetic operations specifies
    left to right sequence.

    You may well have thought that.� You were wrong.� Do you understand that >>> now?


    "x *= ++f * ++f"
    int x = 5;
    int y = 3;

    For the calculation is question is seems to make no difference to the result.
    x = 5 * (4 * 5)
    x = 5 * (5 * 4)



    In this case no, but if the operation was - it would,

    And, as pointed out there is no requirement on the ordering of even the sub-parts except determinism (we can't use a value we haven't computed
    yet). This means the ++ can be interleaved.

    And, because of the EXPLICIT requirement on updates to a value needing
    to be ordered to avoid undefined behavior, the compiler, seeing that Undefined Behavior exist there can do ANYTHING it wants with that code.

    For example, a valid interpretation is to store the value 17 in z.

    --
    Mikko

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to olcott on Sun Jun 23 14:46:09 2024
    On 22/06/2024 18:27, olcott wrote:
    On 6/22/2024 9:52 AM, David Brown wrote:
    On 22/06/2024 14:40, olcott wrote:
    On 6/22/2024 6:09 AM, David Brown wrote:
    On 22/06/2024 06:02, olcott wrote:
    On 6/21/2024 10:42 PM, James Kuyper wrote:
    On 6/21/24 7:06 PM, olcott wrote:
    ...
    When we assume the *= assigns
    the result of the RHS * the LHS to the LHS
    "x *= ++f * ++f"

    means x = x * (++f * ++f)
    thus cannot have implementation defined behavior.

    You are correct about that conclusion, though I don't see what your >>>>>> premise has to do with it. The expression ++f * ++f has, all by
    itself,
    undefined behavior,

    It would seem to be naturally defined to be Left to right
    ++f from 2 to 3
    ++f from 3 to 4
    3 * 4


    Some programming languages define the order of evaluation for
    subexpressions like this.  Neither C nor C++ do (except for specific
    operators, which do not include multiplication).

    It doesn't really matter what you consider "natural" or not - it
    matters what the standards say.


    It would be bad for the standards to be counter-intuitive.


    People's "intuition" varies wildly.  Pretty much any definition would
    be counter-intuitive to someone.  That's why language standards try to
    have precise definitions, rather than just saying "it all works pretty
    much like you'd expect".

    We realise the definition of C and C++ expression evaluation does not
    match what you thought was "intuitive".  That does not matter - it
    would not matter even if lots of C and C++ programmers agreed with
    you.  What matters for these languages is what their standards /say/.
    That way, we can look at clear facts written in official documents,
    rather than trying to rely on what random people think about to be how
    they imagine things ought to be.

    You can happily feel that C would be better if evaluation order were
    strictly define.  Some people will agree with that, others will
    disagree - but it will not make a blind bit of difference to the
    actual /facts/ of the matter.



    Then make the rule strict left to right unless otherwise specified.
    int x = 2 + 3 * 5; // is otherwise specified by operator precedence.



    What do you mean, "Make the rule" ? /I/ didn't write the C++ or C
    standards - I can't make any rules there. Nor can anyone else here.
    And even if I could, I would not change this. C and C++ made these
    design decisions for good reasons, and I agree with the decision.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)