Bug 82071 - Error in assign-ops in combination with FLT_EVAL_METHOD
Summary: Error in assign-ops in combination with FLT_EVAL_METHOD
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 4.8.4
: P3 normal
Target Milestone: 8.0
Assignee: Joseph S. Myers
URL:
Keywords: wrong-code
: 82775 (view as bug list)
Depends on:
Blocks:
 
Reported: 2017-09-01 10:29 UTC by Willem Wakker
Modified: 2022-10-14 07:34 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2017-09-01 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Willem Wakker 2017-09-01 10:29:43 UTC
Compiler:
  Target: i686-linux-gnu
  GNU C (Ubuntu 4.8.4-2ubuntu1~14.04.3) version 4.8.4 (i686-linux-gnu)
  Compile option: -std=c99

The 4 expressions in the program below should give all the same answer.
When using the mentioned compiler that has FLT_EVAL_METHOD=2 gives
3 different answers.

The program:

# include <stdio.h>
# include <float.h>

int
main()
{
        float f = 1.0;
        double d = 1.0;
        int i, j;

#ifdef FLT_EVAL_METHOD
        printf("FLT_EVAL_METHOD: %d\n", FLT_EVAL_METHOD );
#endif

        i = 0x10001234;
        i += f;
        printf("i += f;  \t0x%x\n", i );

        i = 0x10001234;
        i += 1.0f;
        printf("i += 1.0f;\t0x%x\n", i );

        i = 0x10001234;
        i = i + f;
        printf("i = i + f;\t0x%x\n", i );

        i = 0x10001234;
        i = i + 1.0f;
        printf("i = i + 1.0f;\t0x%x\n", i );

}

The results:

FLT_EVAL_METHOD: 2
i += f;         0x10001240
i += 1.0f;      0x10001235
i = i + f;      0x10001241
i = i + 1.0f;   0x10001241

The correct answers is 0x10001241: the expression should be evaluated in long double.
Comment 1 Willem Wakker 2017-09-01 10:50:39 UTC
That's the problem when you have too many answers: the right answer should be 0x10001235
Comment 2 Joseph S. Myers 2017-09-01 12:32:28 UTC
Working on patches (first making compound assignments consistent with arithmetic operators in implementing the understanding of C99 semantics where integer conversions to floating point has no excess precision and 0x10001241 is the right answer, then making binary operators and conditional expressions also support post-N1531 C11 semantics where such implicit conversions have excess precision and 0x10001235 is the right answer).
Comment 3 Joseph S. Myers 2017-09-01 16:30:23 UTC
Author: jsm28
Date: Fri Sep  1 16:29:49 2017
New Revision: 251603

URL: https://gcc.gnu.org/viewcvs?rev=251603&root=gcc&view=rev
Log:
Fix excess precision handling of compound assignments (PR c/82071).

PR c/82071 reports how compound assignment operators such as += handle
excess precision inconsistently with the same operation done with a
plain assignment and binary operator.

There were (at least) two problems with how compound assignments
handled excess precision.  The EXCESS_PRECISION_EXPR for an argument
with excess precision was removed too early, resulting in
build_binary_op being called with an rhs operand whose type reflected
the evaluation format, so not having sufficient information to achieve
the intended semantics in all cases, and then the code called
c_fully_fold on the results of build_binary_op without allowing for
the possibility of an EXCESS_PRECISION_EXPR as the result, so leading
to double rounding of the result (first to its semantic type, then to
the type of the LHS of the assignment) instead of the intended single
rounding.

This patch fixes those problems by keeping EXCESS_PRECISION_EXPRs
further through build_modify_expr (and build_atomic_assign which it
calls) and only removing them locally where appropriate.

Note that while this patch should achieve *consistency*, that's
consistency with the understanding of C99 semantics that I originally
intended to implement.  For the particular case in the testcase, C11
semantics (from N1531) differ from that understanding of C99
semantics, in that an implicit conversion of an integer to floating
point can have excess precision.  I intend to implement those C11
semantics separately (conditional on flag_isoc11) (which will also
mean that building conditional expressions can produce a result with
excess precision even when the arguments lack excess precision, where
previously it could not), and not to close the bug until that is also
done.

Tested for x86_64-pc-linux-gnu.

	PR c/82071
gcc/c:
	* c-typeck.c (build_atomic_assign): Handle argument with excess
	precision.  Ensure any EXCESS_PRECISION_EXPR is present in
	argument passed to build_binary_op and convert_for_assignment but
	not for call to c_fully_fold.
	(build_modify_expr): Do not remove EXCESS_PRECISION_EXPR early.
	Ensure build_binary_op is called with argument with original
	semantic type.  Avoid calling c_fully_fold with an
	EXCESS_PRECISION_EXPR from build_binary_op.

gcc/testsuite:
	* gcc.target/i386/excess-precision-7.c: New test.

Added:
    trunk/gcc/testsuite/gcc.target/i386/excess-precision-7.c
Modified:
    trunk/gcc/c/ChangeLog
    trunk/gcc/c/c-typeck.c
    trunk/gcc/testsuite/ChangeLog
Comment 4 Joseph S. Myers 2017-09-15 20:49:33 UTC
Author: jsm28
Date: Fri Sep 15 20:49:02 2017
New Revision: 252847

URL: https://gcc.gnu.org/viewcvs?rev=252847&root=gcc&view=rev
Log:
Implement C11 excess precision semantics for conversions (PR c/82071).

C11 semantics for excess precision (from N1531) are that an implicit
conversion (from the usual arithmetic conversions, not by assignment)
from integer to floating point has a result in the corresponding
evaluation format of that floating-point type, so possibly with excess
precision (whereas a cast or conversion by assignment from integer to
floating point must produce a value without excess range or precision,
as always).  This patch makes GCC support those semantics if
flag_isoc11 (which in turn means that conditional expressions need to
support generating a result with excess precision even if neither
operand had excess precision).

C99 is less than entirely clear in this regard, but my reading as
outlined at <https://gcc.gnu.org/ml/gcc-patches/2008-11/msg00105.html>
is that the results of conversions from integer to floating-point
types are always expected to be representable in the target type
without excess precision, and this patch conservatively keeps these
semantics for pre-C11 (i.e. if an older standard is explicitly
selected).

Bootstrapped with no regressions on x86_64-pc-linux-gnu.

	PR c/82071

gcc/c:
	* c-typeck.c (ep_convert_and_check): Just call convert_and_check
	for C11.
	(build_conditional_expr): For C11, generate result with excess
	precision when one argument is an integer and the other is of a
	type using excess precision.

gcc/testsuite:
	* gcc.target/i386/excess-precision-8.c: New test.

Added:
    trunk/gcc/testsuite/gcc.target/i386/excess-precision-8.c
Modified:
    trunk/gcc/c/ChangeLog
    trunk/gcc/c/c-typeck.c
    trunk/gcc/testsuite/ChangeLog
Comment 5 Joseph S. Myers 2017-09-15 20:50:59 UTC
Fixed for GCC 8.
Comment 6 Joseph S. Myers 2017-10-30 23:20:04 UTC
*** Bug 82775 has been marked as a duplicate of this bug. ***
Comment 7 GCC Commits 2022-10-14 07:34:40 UTC
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>:

https://gcc.gnu.org/g:16ec267063c8ce60769888d4097bcd158410adc8

commit r13-3291-g16ec267063c8ce60769888d4097bcd158410adc8
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Fri Oct 14 09:33:23 2022 +0200

    c++: Excess precision for ? int : float or int == float [PR107097, PR82071, PR87390]
    
    The following incremental patch implements the C11 behavior (for all C++
    versions) for
    cond ? int : float
    cond ? float : int
    int cmp float
    float cmp int
    where int is any integral type, float any floating point type with
    excess precision and cmp ==, !=, >, <, >=, <= and <=>.
    
    2022-10-14  Jakub Jelinek  <jakub@redhat.com>
    
            PR c/82071
            PR c/87390
            PR c++/107097
    gcc/cp/
            * cp-tree.h (cp_ep_convert_and_check): Remove.
            * cvt.cc (cp_ep_convert_and_check): Remove.
            * call.cc (build_conditional_expr): Use excess precision for ?: with
            one arm floating and another integral.  Don't convert first to
            semantic result type from integral types.
            (convert_like_internal): Don't call cp_ep_convert_and_check, instead
            just strip EXCESS_PRECISION_EXPR before calling cp_convert_and_check
            or cp_convert.
            * typeck.cc (cp_build_binary_op): Set may_need_excess_precision
            for comparisons or SPACESHIP_EXPR with at least one operand integral.
            Don't compute semantic_result_type if build_type is non-NULL.  Call
            cp_convert_and_check instead of cp_ep_convert_and_check.
    gcc/testsuite/
            * gcc.target/i386/excess-precision-8.c: For C++ wrap abort and
            exit declarations into extern "C" block.
            * gcc.target/i386/excess-precision-10.c: Likewise.
            * g++.target/i386/excess-precision-7.C: Remove.
            * g++.target/i386/excess-precision-8.C: New test.
            * g++.target/i386/excess-precision-9.C: Remove.
            * g++.target/i386/excess-precision-10.C: New test.
            * g++.target/i386/excess-precision-12.C: New test.