This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: optimization/9736: same fp comparison can lead to different results


http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gcc&pr=9736

>From various parts of the ISO/IEC C++ standard:

  5 Expressions

  ...

  Many binary operators that expect operands of
  arithmetic or enumeration type cause conversions
  and yield result types in a similar way. The
  purpose is to yield a common type, which is also
  the type of the result. This pattern is called
  the usual arithmetic conversions, which are
  defined as follows:

    If either operand is of type long double, the
    other shall be converted to long double.

    Otherwise, if either operand is double, the
    other shall be converted to double.

    Otherwise, if either operand is float, the
    other shall be converted to float.

    ...

  The values of the floating operands and the
  results of floating expressions may be
  represented in greater precision and range than
  that required by the type; the types are not
  changed thereby.  55)

  ...

  55) The cast and assignment operators must still
  perform their specific conversions as described
  in 5.4, 5.2.9 and 5.17.

...

  5.17 Assignment operators

  There are several assignment operators, all of
  which group right-to-left. All require a
  modifiable lvalue as their left operand, and the
  type of an assignment expression is that of its
  left operand. The result of the assignment
  operation is the value stored in the left
  operand after the assignment has taken place;
  the result is an lvalue.

  ...

  In simple assignment (=), the value of the
  expression replaces that of the object referred
  to by the left operand.

...

  4 Standard conversions

  ... A standard conversion sequence will be
  applied to an expression if necessary to convert
  it to a required destination type.

  [Note: expressions with a given type will be
  implicitly converted to other types in several
  contexts:

  -- When used as operands of operators. The
  operator's requirements for its operands dictate
  the destination type (clause 5).

  ...

  -- When used as the source expression for an
  initialization (which includes use as an
  argument in a function call and use as the
  expression in a return statement). The type of
  the entity being initialized is (generally) the
  destination type. See 8.5, 8.5.3.

  -- end note]

...

  4.8 Floating point conversions

  An rvalue of floating point type can be
  converted to an rvalue of another floating point
  type. If the source value can be exactly
  represented in the destination type, the result
  of the conversion is that exact
  representation. If the source value is between
  two adjacent destination values, the result of
  the conversion is an implementation-defined
  choice of either of those values. Otherwise, the
  behavior is undefined.




Consider this small test program:

// original.cc:
int f(float x, float y, float z, float w)
{
  float v = x*x+y*y+z*z+w;
  int test1 = v < 1.0f ? 1 : 2;
  return test1;
}
int main(void)
{
  return f(0.5f, 0.5f, 0.5f, 0.25f-1.0f/67108864.0f);
}

The standard allows the intermediate calculations
for v to be at greater precision than float,
although the types are still considered to be
float.  However, when v itself is initialized,
there is an implicit standard conversion to float.
Thus, we are allowed rewrite the small test
program as this (where the types represent the
actual precision):

// transformed.cc:
int f(float x, float y, float z, float w)
{
  const long double temp_x = x;
  const long double temp_y = y;
  const long double temp_z = z;
  const long double temp_w = w;
  const long double temp_xx = temp_x*temp_x;
  const long double temp_yy = temp_y*temp_y;
  const long double temp_xxpyy = temp_xx+temp_yy;
  const long double temp_zz = temp_z*temp_z;
  const long double temp_xxpyypzz = temp_xxpyy+temp_zz;
  const long double temp_xxpyypzzpw = temp_xxpyypzz+temp_w;
  float v = temp_xxpyypzzpw;
  int test1 = v < 1.0f ? 1 : 2;
  return test1;
}
int main(void)
{
  return f(0.5f, 0.5f, 0.5f, 0.25f-1.0f/67108864.0f);
}

I expect both programs to initialize v to 1.0f
(and return 2 from f() and main()).  However, with
-O1, original.cc is not compiled according to the
standard.

[Paradoxically, transformed.cc is compiled
correctly with -O1.  The generated code is
essentially the same except for transformed.cc
that the proper conversion to float is generated
for initializing v.]


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]