llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.13k stars 12.02k forks source link

FP constant expressions evaluated incorrectly with FLT_EVAL_METHOD == 2 #89128

Open jcranmer-intel opened 7 months ago

jcranmer-intel commented 7 months ago

On systems with FLT_EVAL_METHOD == 2 (i.e., x87-based floating-point computation), arithmetic expressions using float and double should be evaluated using long double instead. Clang isn't doing this:

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

static double const_init = 1.0 + (DBL_EPSILON/2) + (DBL_EPSILON/2);
int main() {
    double nonconst_init = 1.0;
    nonconst_init = nonconst_init + (DBL_EPSILON/2) + (DBL_EPSILON/2);
    printf("FLT_EVAL_METHOD = %d\n", FLT_EVAL_METHOD);
    printf("const: %g\n", const_init - 1.0);
    printf("nonconst: %g\n", (double)nonconst_init - 1.0);
}

When compiled on an x86 system with -m32 -mno-sse, the correct result should be that the two lines both output 2.22045e-16, which icc is able to successfully do. However clang and gcc both report 0 for the first line and 2.22045e-16 for the second: https://godbolt.org/z/4xGoaaoKP.

(At first glance, this sounds exactly like https://github.com/llvm/llvm-project/issues/88956, but that is about constants and this is about constant expressions).

C99 section 6.6p5 says

If a floating expression is evaluated in the translation environment, the arithmetic precision and range shall be at least as great as if the expression were being evaluated in the execution environment.

C11 adds a footnote explicitly saying that FLT_EVAL_METHOD also applies to the translation environment, and C23 adds DEC_EVAL_METHOD to the footnote, but otherwise the text remains the same in all versions of C.

llvmbot commented 7 months ago

@llvm/issue-subscribers-clang-frontend

Author: Joshua Cranmer (jcranmer-intel)

On systems with FLT_EVAL_METHOD == 2 (i.e., x87-based floating-point computation), arithmetic expressions using `float` and `double` should be evaluated using `long double` instead. Clang isn't doing this: ```c #include <float.h> #include <stdio.h> static double const_init = 1.0 + (DBL_EPSILON/2) + (DBL_EPSILON/2); int main() { double nonconst_init = 1.0; nonconst_init = nonconst_init + (DBL_EPSILON/2) + (DBL_EPSILON/2); printf("FLT_EVAL_METHOD = %d\n", FLT_EVAL_METHOD); printf("const: %g\n", const_init - 1.0); printf("nonconst: %g\n", (double)nonconst_init - 1.0); } ``` When compiled on an x86 system with `-m32 -mno-sse`, the correct result should be that the two lines both output `2.22045e-16`, which icc is able to successfully do. However clang and gcc both report 0 for the first line and `2.22045e-16` for the second: https://godbolt.org/z/4xGoaaoKP. (At first glance, this sounds exactly like https://github.com/llvm/llvm-project/issues/88956, but that is about *constants* and this is about constant *expressions*). C99 section 6.6p5 says > If a floating expression is evaluated in the translation environment, the arithmetic precision and range shall be at least as great as if the expression were being evaluated in the execution environment. C11 adds a footnote explicitly saying that `FLT_EVAL_METHOD` also applies to the translation environment, and C23 adds `DEC_EVAL_METHOD` to the footnote, but otherwise the text remains the same in all versions of C.
vinc17fr commented 7 months ago

When compiled on an x86 system with -m32 -mno-sse, the correct result should be that the two lines both output 2.22045e-16, which icc is able to successfully do. However clang and gcc both report 0 for the first line and 2.22045e-16 for the second: https://godbolt.org/z/4xGoaaoKP.

For GCC, you need an option either like -std=c17 or -fexcess-precision=standard to tell it to conform to the ISO C standard. For instance, with -std=c17, I get 2.22045e-16 on both lines, which is correct.

That said, I would regard the GCC behavior without -std=c17 or -fexcess-precision=standard as a bug (similar to the one for constants).