sequence points and evaluation order

Discussion in 'C Programming' started by John Smith, Sep 4, 2006.

  1. John Smith

    John Smith Guest

    I've been playing with splint, which returns the following
    warning for the code below:

    statlib.c: (in function log_norm_pdf)
    statlib.c(1054,31): Expression has undefined behavior (left
    operand uses errno,
    modified by right operand): (log(x) - mu) * (log(x) - mu)
    Code has unspecified behavior. Order of evaluation of function
    parameters or
    subexpressions is not defined, so if a value is used and
    modified in
    different places not separated by a sequence point
    constraining evaluation
    order, then the result of the expression is unspecified.

    How can the offending expression be written so that the order of
    evaluation is properly specified? Can t2num be assigned the value
    of any single expression equivalent to ((log(x)-mu) ^ 2) * -1.0?


    /* Return log normal probability density at x.
    mu=a_mean(ln(X)); sigma=s_stdev(ln(X)) (output OK) */
    double log_norm_pdf(double x, double mu, double sigma)
    {
    double t1, t2num, t2den;

    if(x <= 0.0) return 0.0;

    t1 = 1.0 / (sqrt(2.0 * PI) * sigma * x);
    t2num = -((log(x) - mu) * (log(x) - mu)); /* problem here */
    t2den = (2.0 * (sigma * sigma));

    return t1 * exp(t2num / t2den);
    }

    BTW: this function has been rigorously tested.
     
    John Smith, Sep 4, 2006
    #1
    1. Advertising

  2. John Smith wrote:

    > I've been playing with splint, which returns the following
    > warning for the code below:
    >
    > statlib.c: (in function log_norm_pdf)
    > statlib.c(1054,31): Expression has undefined behavior (left
    > operand uses errno,
    > modified by right operand): (log(x) - mu) * (log(x) - mu)
    > Code has unspecified behavior. Order of evaluation of function
    > parameters or
    > subexpressions is not defined, so if a value is used and
    > modified in
    > different places not separated by a sequence point
    > constraining evaluation
    > order, then the result of the expression is unspecified.


    I'm a bit puzzled by this.
    Is it unspecified or undefined ? And why is it either ?
    When it says "operand uses errno" does it mean reads
    the value of errno or it means reads or writes the value
    of errno ?

    >
    > How can the offending expression be written so that the order of
    > evaluation is properly specified? Can t2num be assigned the value
    > of any single expression equivalent to ((log(x)-mu) ^ 2) * -1.0?
    >
    >
    > /* Return log normal probability density at x.
    > mu=a_mean(ln(X)); sigma=s_stdev(ln(X)) (output OK) */
    > double log_norm_pdf(double x, double mu, double sigma)
    > {
    > double t1, t2num, t2den;
    >
    > if(x <= 0.0) return 0.0;
    >
    > t1 = 1.0 / (sqrt(2.0 * PI) * sigma * x);
    > t2num = -((log(x) - mu) * (log(x) - mu)); /* problem here */


    You can write this as
    t2num = -( a = log(x) - mu , a * a) ;
    Obviously a needs to be defined.
    You can even write
    { double a ;
    t2num = -( a = log(x) - mu , a * a) ;
    }
    to let the compiler know that the value of a
    won't be needed anymore. Perhaps it will
    affect optimization.

    > t2den = (2.0 * (sigma * sigma));
    >
    > return t1 * exp(t2num / t2den);
    > }
    >
    > BTW: this function has been rigorously tested.
     
    Spiros Bousbouras, Sep 4, 2006
    #2
    1. Advertising

  3. John Smith wrote:
    > I've been playing with splint, which returns the following
    > warning for the code below:
    >
    > statlib.c: (in function log_norm_pdf)
    > statlib.c(1054,31): Expression has undefined behavior (left
    > operand uses errno,
    > modified by right operand): (log(x) - mu) * (log(x) - mu)
    > Code has unspecified behavior. Order of evaluation of function
    > parameters or
    > subexpressions is not defined, so if a value is used and
    > modified in
    > different places not separated by a sequence point
    > constraining evaluation
    > order, then the result of the expression is unspecified.
    >
    > How can the offending expression be written so that the order of
    > evaluation is properly specified? Can t2num be assigned the value
    > of any single expression equivalent to ((log(x)-mu) ^ 2) * -1.0?
    >
    >
    > /* Return log normal probability density at x.
    > mu=a_mean(ln(X)); sigma=s_stdev(ln(X)) (output OK) */
    > double log_norm_pdf(double x, double mu, double sigma)
    > {
    > double t1, t2num, t2den;
    >
    > if(x <= 0.0) return 0.0;
    >
    > t1 = 1.0 / (sqrt(2.0 * PI) * sigma * x);
    > t2num = -((log(x) - mu) * (log(x) - mu)); /* problem here */


    What splint is trying to tell you is that the value of errno is
    dependent on the order of evaluation of the 2 function calls. There is
    no undefined behavior here (there is a sequence point between any
    possible modifications to errno) and since you are not using errno
    there isn't anything to worry about. You could use the value of errno,
    it's value is not indeterminate, but you just can't be sure which call
    to log() is the one that set the current value of errno. If you want
    to get rid of the splint message you could store the value of "log(x) -
    mu" in a temporary variable and do the multiplication seperately
    although you might incur a precision hit. Your best bet is probably to
    disable the warning inline for this particular case:

    /* @-evalorder@ */
    t2num = -((log(x) - mu) * (log(x) - mu)); /* problem here */
    /* @+evalorder@ */

    Robert Gamble
     
    Robert Gamble, Sep 4, 2006
    #3
  4. John Smith

    Flash Gordon Guest

    Spiros Bousbouras wrote:
    > John Smith wrote:
    >
    >> I've been playing with splint, which returns the following
    >> warning for the code below:
    >>
    >> statlib.c: (in function log_norm_pdf)
    >> statlib.c(1054,31): Expression has undefined behavior (left
    >> operand uses errno,
    >> modified by right operand): (log(x) - mu) * (log(x) - mu)
    >> Code has unspecified behavior. Order of evaluation of function
    >> parameters or
    >> subexpressions is not defined, so if a value is used and
    >> modified in
    >> different places not separated by a sequence point
    >> constraining evaluation
    >> order, then the result of the expression is unspecified.

    >
    > I'm a bit puzzled by this.
    > Is it unspecified or undefined ? And why is it either ?
    > When it says "operand uses errno" does it mean reads
    > the value of errno or it means reads or writes the value
    > of errno ?


    The order of the two function calls is unspecified, but IIRC there is a
    sequence point on calling a function, and the two calls are not
    permitted to be run in parallel, so I can't see any undefined behaviour.
    If either call sets errno it is guaranteed that errno will be set to a
    non-0 value (standard functions do not set it to 0) so the code can
    still test errno to see if an error has occurred and what at least one
    error was. So I really don't see a problem.

    >> How can the offending expression be written so that the order of
    >> evaluation is properly specified? Can t2num be assigned the value
    >> of any single expression equivalent to ((log(x)-mu) ^ 2) * -1.0?
    >>
    >>
    >> /* Return log normal probability density at x.
    >> mu=a_mean(ln(X)); sigma=s_stdev(ln(X)) (output OK) */
    >> double log_norm_pdf(double x, double mu, double sigma)
    >> {
    >> double t1, t2num, t2den;
    >>
    >> if(x <= 0.0) return 0.0;
    >>
    >> t1 = 1.0 / (sqrt(2.0 * PI) * sigma * x);
    >> t2num = -((log(x) - mu) * (log(x) - mu)); /* problem here */

    >
    > You can write this as
    > t2num = -( a = log(x) - mu , a * a) ;
    > Obviously a needs to be defined.
    > You can even write
    > { double a ;
    > t2num = -( a = log(x) - mu , a * a) ;
    > }
    > to let the compiler know that the value of a
    > won't be needed anymore. Perhaps it will
    > affect optimization.


    Personally I would do something more like
    {
    double a = log(x) - mu;
    t2num -= a * a;
    }

    In my opinion this is easier to read. I don't see any benefit in using
    the comma operator for this. Alternatively, of course, have it defined
    earlier. I would consider calling log with the same value twice to be a
    needless introduction of inefficiency and one that is less likely to be
    fixed by the compiler than many.

    For similar reasons I would probably #define ROOT_2PI

    >> t2den = (2.0 * (sigma * sigma));


    Needless bracketing which, in my opinion, does not make it easier to read.

    >> return t1 * exp(t2num / t2den);


    When assigning t1 I would not have used 1.0/whatever, instead I would
    divide by t1 here.

    >> }
    >>
    >> BTW: this function has been rigorously tested.


    It may test OK but it would not have got through a code review with me
    without changes or *very* good technical explanations of why they should
    not be made.
    --
    Flash Gordon
     
    Flash Gordon, Sep 4, 2006
    #4
  5. Flash Gordon <> writes:
    > Spiros Bousbouras wrote:
    >> John Smith wrote:
    >>
    >>> I've been playing with splint, which returns the following
    >>> warning for the code below:
    >>>
    >>> statlib.c: (in function log_norm_pdf)
    >>> statlib.c(1054,31): Expression has undefined behavior (left
    >>> operand uses errno,
    >>> modified by right operand): (log(x) - mu) * (log(x) - mu)
    >>> Code has unspecified behavior. Order of evaluation of function
    >>> parameters or
    >>> subexpressions is not defined, so if a value is used and
    >>> modified in
    >>> different places not separated by a sequence point
    >>> constraining evaluation
    >>> order, then the result of the expression is unspecified.

    >> I'm a bit puzzled by this.
    >> Is it unspecified or undefined ? And why is it either ?
    >> When it says "operand uses errno" does it mean reads
    >> the value of errno or it means reads or writes the value
    >> of errno ?

    >
    > The order of the two function calls is unspecified, but IIRC there is
    > a sequence point on calling a function, and the two calls are not
    > permitted to be run in parallel, so I can't see any undefined
    > behaviour. If either call sets errno it is guaranteed that errno will
    > be set to a non-0 value (standard functions do not set it to 0) so the
    > code can still test errno to see if an error has occurred and what at
    > least one error was. So I really don't see a problem.

    [...]

    If log() is implemented as a macro, there may not be a sequence point.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Sep 5, 2006
    #5
  6. Keith Thompson wrote:
    > Flash Gordon <> writes:
    > > Spiros Bousbouras wrote:
    > >> John Smith wrote:
    > >>
    > >>> I've been playing with splint, which returns the following
    > >>> warning for the code below:
    > >>>
    > >>> statlib.c: (in function log_norm_pdf)
    > >>> statlib.c(1054,31): Expression has undefined behavior (left
    > >>> operand uses errno,
    > >>> modified by right operand): (log(x) - mu) * (log(x) - mu)
    > >>> Code has unspecified behavior. Order of evaluation of function
    > >>> parameters or
    > >>> subexpressions is not defined, so if a value is used and
    > >>> modified in
    > >>> different places not separated by a sequence point
    > >>> constraining evaluation
    > >>> order, then the result of the expression is unspecified.
    > >> I'm a bit puzzled by this.
    > >> Is it unspecified or undefined ? And why is it either ?
    > >> When it says "operand uses errno" does it mean reads
    > >> the value of errno or it means reads or writes the value
    > >> of errno ?

    > >
    > > The order of the two function calls is unspecified, but IIRC there is
    > > a sequence point on calling a function, and the two calls are not
    > > permitted to be run in parallel, so I can't see any undefined
    > > behaviour. If either call sets errno it is guaranteed that errno will
    > > be set to a non-0 value (standard functions do not set it to 0) so the
    > > code can still test errno to see if an error has occurred and what at
    > > least one error was. So I really don't see a problem.

    > [...]
    >
    > If log() is implemented as a macro, there may not be a sequence point.


    This is true, although it would be quite difficult to imagine a macro
    version that calculates the log and sets errno all without introducing
    a sequence point. In any case, it is definitely a QOI issue although I
    find it somewhat disturbing that the Standard allows this. I wonder
    why the Standard committee didn't stick in something that prohibits the
    modification of an object with static duration from a macro version of
    a library function without appropriate sequence points.

    Robert Gamble
     
    Robert Gamble, Sep 5, 2006
    #6
  7. John Smith

    Guest

    Robert Gamble wrote:
    > Keith Thompson wrote:
    > > Flash Gordon <> writes:
    > > > Spiros Bousbouras wrote:
    > > >> John Smith wrote:
    > > >>
    > > >>> I've been playing with splint, which returns the following
    > > >>> warning for the code below:
    > > >>>
    > > >>> statlib.c: (in function log_norm_pdf)
    > > >>> statlib.c(1054,31): Expression has undefined behavior (left
    > > >>> operand uses errno,
    > > >>> modified by right operand): (log(x) - mu) * (log(x) - mu)
    > > >>> Code has unspecified behavior. Order of evaluation of function
    > > >>> parameters or
    > > >>> subexpressions is not defined, so if a value is used and
    > > >>> modified in
    > > >>> different places not separated by a sequence point
    > > >>> constraining evaluation
    > > >>> order, then the result of the expression is unspecified.
    > > >> I'm a bit puzzled by this.
    > > >> Is it unspecified or undefined ? And why is it either ?
    > > >> When it says "operand uses errno" does it mean reads
    > > >> the value of errno or it means reads or writes the value
    > > >> of errno ?
    > > >
    > > > The order of the two function calls is unspecified, but IIRC there is
    > > > a sequence point on calling a function, and the two calls are not
    > > > permitted to be run in parallel, so I can't see any undefined
    > > > behaviour. If either call sets errno it is guaranteed that errno will
    > > > be set to a non-0 value (standard functions do not set it to 0) so the
    > > > code can still test errno to see if an error has occurred and what at
    > > > least one error was. So I really don't see a problem.

    > > [...]
    > >
    > > If log() is implemented as a macro, there may not be a sequence point.

    >
    > This is true, although it would be quite difficult to imagine a macro
    > version that calculates the log and sets errno all without introducing
    > a sequence point.


    Despite the evidence of the error message above? That
    makes it pretty easy to imagine I would say.

    > In any case, it is definitely a QOI issue although I
    > find it somewhat disturbing that the Standard allows this. I wonder
    > why the Standard committee didn't stick in something that prohibits the
    > modification of an object with static duration from a macro version of
    > a library function without appropriate sequence points.


    Better still, a requirement that a macro version behave
    as if the actual function would.
     
    , Sep 6, 2006
    #7
  8. John Smith

    Spoon Guest

    Flash Gordon wrote:

    > Personally I would do something more like
    > {
    > double a = log(x) - mu;
    > t2num -= a * a;
    > }


    Is -= a typo?

    The original code was:

    t2num = -((log(x) - mu) * (log(x) - mu));

    (with t2num uninitialized.)

    Isn't
    t2num -= a * a;
    equivalent to
    t2num = t2num - (a * a);
    ?
     
    Spoon, Sep 6, 2006
    #8
  9. John Smith

    Flash Gordon Guest

    Spoon wrote:
    > Flash Gordon wrote:
    >
    >> Personally I would do something more like
    >> {
    >> double a = log(x) - mu;
    >> t2num -= a * a;
    >> }

    >
    > Is -= a typo?


    <snip>

    No, it was a reado. I wasn't reading carefully enough.However, the
    general point still stands.
    --
    Flash Gordon
     
    Flash Gordon, Sep 6, 2006
    #9
  10. wrote:
    > Robert Gamble wrote:
    > > Keith Thompson wrote:
    > > > Flash Gordon <> writes:
    > > > > Spiros Bousbouras wrote:
    > > > >> John Smith wrote:
    > > > >>
    > > > >>> I've been playing with splint, which returns the following
    > > > >>> warning for the code below:
    > > > >>>
    > > > >>> statlib.c: (in function log_norm_pdf)
    > > > >>> statlib.c(1054,31): Expression has undefined behavior (left
    > > > >>> operand uses errno,
    > > > >>> modified by right operand): (log(x) - mu) * (log(x) - mu)
    > > > >>> Code has unspecified behavior. Order of evaluation of function
    > > > >>> parameters or
    > > > >>> subexpressions is not defined, so if a value is used and
    > > > >>> modified in
    > > > >>> different places not separated by a sequence point
    > > > >>> constraining evaluation
    > > > >>> order, then the result of the expression is unspecified.
    > > > >> I'm a bit puzzled by this.
    > > > >> Is it unspecified or undefined ? And why is it either ?
    > > > >> When it says "operand uses errno" does it mean reads
    > > > >> the value of errno or it means reads or writes the value
    > > > >> of errno ?
    > > > >
    > > > > The order of the two function calls is unspecified, but IIRC there is
    > > > > a sequence point on calling a function, and the two calls are not
    > > > > permitted to be run in parallel, so I can't see any undefined
    > > > > behaviour. If either call sets errno it is guaranteed that errno will
    > > > > be set to a non-0 value (standard functions do not set it to 0) so the
    > > > > code can still test errno to see if an error has occurred and what at
    > > > > least one error was. So I really don't see a problem.
    > > > [...]
    > > >
    > > > If log() is implemented as a macro, there may not be a sequence point.

    > >
    > > This is true, although it would be quite difficult to imagine a macro
    > > version that calculates the log and sets errno all without introducing
    > > a sequence point.

    >
    > Despite the evidence of the error message above? That
    > makes it pretty easy to imagine I would say.


    It's not too difficult to imagine *a* function that would do this as
    virtually any library function may set errno, but it would be difficult
    to imagine a conforming implementation of the log function that does as
    its implementation is non-trivial and it returns a non-int value.

    > > In any case, it is definitely a QOI issue although I
    > > find it somewhat disturbing that the Standard allows this. I wonder
    > > why the Standard committee didn't stick in something that prohibits the
    > > modification of an object with static duration from a macro version of
    > > a library function without appropriate sequence points.

    >
    > Better still, a requirement that a macro version behave
    > as if the actual function would.


    There is a sequence point before any function in C returns. Requiring
    that all macro versions of library functions have this sequence point
    would unnecessarily complicate and invalidate many existing macros
    such as those covering functions in ctype.h. As long as the macro does
    not modify objects with static duration I don't see a need for forcing
    the macro version to implement the sequence point.

    Robert Gamble
     
    Robert Gamble, Sep 6, 2006
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Divick
    Replies:
    2
    Views:
    323
    Divick
    Sep 5, 2006
  2. Dave Rahardja
    Replies:
    8
    Views:
    412
    Dave Rahardja
    Jul 5, 2007
  3. ais523
    Replies:
    9
    Views:
    374
    Kaz Kylheku
    Feb 9, 2008
  4. George

    sequence points and the execution model

    George, Jan 17, 2009, in forum: C Programming
    Replies:
    7
    Views:
    323
    Richard
    Jan 20, 2009
  5. cmelliso

    sequence points and expression evaluation

    cmelliso, Jul 2, 2010, in forum: C Programming
    Replies:
    10
    Views:
    534
    cmelliso
    Jul 10, 2010
Loading...

Share This Page