llvm / llvm-project

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

FLT_EVAL_METHOD = 2 is not honored for floating-point constants #88956

Open vinc17fr opened 4 months ago

vinc17fr commented 4 months ago

The ISO C17 standard says in 5.2.4.2.2p9, when FLT_EVAL_METHOD is 2: "evaluate all operations and constants to the range and precision of the long double type." (note the word "constants"). But Debian clang version 18.1.3 (1) does not behave that way:

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

int main (void)
{
  printf ("FLT_EVAL_METHOD = %d\n", FLT_EVAL_METHOD);
  printf ("%La\n%La\n", 1e-8L, (long double) 1e-8);
  printf ("%a\n%a\n", (double) 1e-8, (double) 1e-8f);
  return 0;
}

gives with -m32 -std=c17 on x86_64:

FLT_EVAL_METHOD = 2
0xa.bcc77118461cefdp-30
0xa.bcc77118461dp-30
0x1.5798ee2308c3ap-27
0x1.5798eep-27

instead of

FLT_EVAL_METHOD = 2
0xa.bcc77118461cefdp-30
0xa.bcc77118461cefdp-30
0x1.5798ee2308c3ap-27
0x1.5798ee2308c3ap-27
andykaylor commented 4 months ago

It's not clear to me what it means to "evaluate a constant" if you're talking about a constant that isn't used as part of a constant expression. Elsewhere the standard says, "The translation-time conversion of floating constants should match the execution-time conversion of character strings by library functions, such as strtod, given matching inputs suitable for both conversions, the same result format, and default execution-time rounding." But this is listed as a recommended practice.

It's not clear to me that 0.1f (for example) should be represented the same way that 0.1L would be, even when FLT_EVAL_METHOD is 2. I'm inclined to regard the evaluation of a constant as referring to how constant expressions are evaluated (for example, "10.0f/1.0f"). It seems that gcc interprets it as you do, translating literal constants directly to the higher precision type. The Intel Classic Compiler (icc) uses the single-precision rendering of the literal, promoted to extended precision, but evaluates constant expressions at extended precision. Clang doesn't seem to apply the eval method to constants in any way.

https://godbolt.org/z/4KvvM8xEe

vinc17fr commented 4 months ago

It's not clear to me what it means to "evaluate a constant" if you're talking about a constant that isn't used as part of a constant expression.

This is the translation-time conversion of the constant.

Elsewhere the standard says, "The translation-time conversion of floating constants should match the execution-time conversion of character strings by library functions, such as strtod, given matching inputs suitable for both conversions, the same result format, and default execution-time rounding." But this is listed as a recommended practice.

Note that above this paragraph: "For decimal floating constants, and also for hexadecimal floating constants when FLT_RADIX is not a power of 2, the result is either the nearest representable value, or the larger or smaller representable value immediately adjacent to the nearest representable value, chosen in an implementation-defined manner." And with FLT_EVAL_METHOD = 2, the evaluation type is long double. So, blindly using a smaller format is wrong.

It's not clear to me that 0.1f (for example) should be represented the same way that 0.1L would be, even when FLT_EVAL_METHOD is 2.

Not necessarily, but it must be close enough to the nearest representable value in long double. And with Annex F, correct rounding is required for this constant.

I'm inclined to regard the evaluation of a constant as referring to how constant expressions are evaluated (for example, "10.0f/1.0f"). It seems that gcc interprets it as you do, translating literal constants directly to the higher precision type. The Intel Classic Compiler (icc) uses the single-precision rendering of the literal, promoted to extended precision, but evaluates constant expressions at extended precision.

I would say that icc is buggy.

Clang doesn't seem to apply the eval method to constants in any way.

It does, but on this machine on godbolt, FLT_EVAL_METHOD = 0 instead of 2. Thus you get the behavior for FLT_EVAL_METHOD = 0.

llvmbot commented 4 months ago

@llvm/issue-subscribers-clang-frontend

Author: Vincent Lefèvre (vinc17fr)

The ISO C17 standard says in 5.2.4.2.2p9, when `FLT_EVAL_METHOD` is 2: "evaluate all operations and constants to the range and precision of the long double type." (note the word "constants"). But Debian clang version 18.1.3 (1) does not behave that way: ``` #include <stdio.h> #include <float.h> int main (void) { printf ("FLT_EVAL_METHOD = %d\n", FLT_EVAL_METHOD); printf ("%La\n%La\n", 1e-8L, (long double) 1e-8); printf ("%a\n%a\n", (double) 1e-8, (double) 1e-8f); return 0; } ``` gives with `-m32 -std=c17` on x86_64: ``` FLT_EVAL_METHOD = 2 0xa.bcc77118461cefdp-30 0xa.bcc77118461dp-30 0x1.5798ee2308c3ap-27 0x1.5798eep-27 ``` instead of ``` FLT_EVAL_METHOD = 2 0xa.bcc77118461cefdp-30 0xa.bcc77118461cefdp-30 0x1.5798ee2308c3ap-27 0x1.5798ee2308c3ap-27 ```
AaronBallman commented 4 months ago

It's not clear to me what it means to "evaluate a constant" if you're talking about a constant that isn't used as part of a constant expression.

C23 5.1.2.4p2: Evaluation of an expression in general includes both value computations and initiation of side effects. Value computation for an lvalue expression includes determining the identity of the designated object.

So evaluating a constant means getting the value computation of it, which I believe is subject to FLT_EVAL_METHOD.

Elsewhere the standard says, "The translation-time conversion of floating constants should match the execution-time conversion of character strings by library functions, such as strtod, given matching inputs suitable for both conversions, the same result format, and default execution-time rounding." But this is listed as a recommended practice.

We should be following recommended practices unless we have a good reason not to.

vinc17fr commented 4 months ago

Elsewhere the standard says, "The translation-time conversion of floating constants should match the execution-time conversion of character strings by library functions, such as strtod, given matching inputs suitable for both conversions, the same result format, and default execution-time rounding." But this is listed as a recommended practice.

We should be following recommended practices unless we have a good reason not to.

Note that before considering this recommended practice, the destination format/type has to be determined, which is the point of this bug.

jcranmer-intel commented 4 months ago

It does, but on this machine on godbolt, FLT_EVAL_METHOD = 0 instead of 2. Thus you get the behavior for FLT_EVAL_METHOD = 0.

FWIW, -m32 -mno-sse generally gets you to FLT_EVAL_METHOD=2.

andykaylor commented 4 months ago

I should clarify that I'm in no way opposed to fixing this behavior in clang. I'm happy to accept the interpretation that's been described here, and I appreciate the explanation.

AaronBallman commented 4 months ago

Do folks want this question raised with the C floating point study group or WG14? If so, I can ask on the reflectors (or @jcranmer-intel could ask at the next SG meeting, perhaps).