6

The following program (adapted from here) is giving inconsistent results when compiled with GCC (4.8.2) and Clang (3.5.1). In particular, the GCC result does not change even when FLT_EVAL_METHOD does.

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

int r1;
double ten = 10.0;

int main(int c, char **v) {
  printf("FLT_EVAL_METHOD = %d\n", FLT_EVAL_METHOD);
  r1 = 0.1 == (1.0 / ten);
  printf("0.1 = %a, 1.0/ten = %a\n", 0.1, 1.0 / ten);
  printf("r1=%d\n", r1);
}

Tests:

$ gcc -std=c99 t.c && ./a.out
FLT_EVAL_METHOD = 0
0.1 = 0x1.999999999999ap-4, 1.0/ten = 0x1.999999999999ap-4
r1=1

$ gcc -std=c99 -mfpmath=387 t.c && ./a.out
FLT_EVAL_METHOD = 2
0.1 = 0x0.0000000000001p-1022, 1.0/ten = 0x0p+0
r1=1

$ clang -std=c99 t.c && ./a.out
FLT_EVAL_METHOD = 0
0.1 = 0x1.999999999999ap-4, 1.0/ten = 0x1.999999999999ap-4
r1=1

$ clang -std=c99 -mfpmath=387 -mno-sse t.c && ./a.out
FLT_EVAL_METHOD = 2
0.1 = 0x0.07fff00000001p-1022, 1.0/ten = 0x0p+0
r1=0

Note that, according to this blog post, GCC 4.4.3 used to output 0 instead of 1 in the second test.

A possibly related question indicates that a bug has been corrected in GCC 4.6, which might explain why GCC's result is different.

I would like to confirm if any of these results would be incorrect, or if some subtle evaluation steps (e.g. a new preprocessor optimization) would justify the difference between these compilers.

malat
  • 12,152
  • 13
  • 89
  • 158
anol
  • 8,264
  • 3
  • 34
  • 78
  • 2
    I have had to investigate the behavior of old GCC versions recently and someone pointed me to https://gcc.godbolt.org which was very useful. It does not have GCC 4.4.3, but it has 4.4.7. – Pascal Cuoq Mar 04 '15 at 10:04
  • Was `gcc -std=c99 -mpfmath=387 t.c && ./a.out` really `gcc -std=c99 -mfpmath=387 t.c && ./a.out` (typo?) – chux - Reinstate Monica Jun 30 '20 at 15:22

2 Answers2

6

This answer is about something that you should resolve before you go further, because it is going to make reasoning about what happens much harder otherwise:

Surely printing 0.1 = 0x0.07fff00000001p-1022 or 0.1 = 0x0.0000000000001p-1022 can only be a bug on your compilation platform caused by ABI mismatch when using -mfpmath=387. None of these values can be excused by excess precision.

You could try to include your own conversion-to-readable-format in the test file, so that that conversion is also compiled with -mfpmath=387. Or make a small stub in another file, not compiled with that option, with a minimalistic call convention:

In other file:

double d;
void print_double(void)
{
  printf("%a", d);
}

In the file compiled with -mfpmath=387:

extern double d;
d = 0.1;
print_double();
CuriosGuy
  • 181
  • 1
  • 9
Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • Indeed, compiling first the `print_double` file without `-mfpmath=387`, and then linking its `.o` to the other file solves the issue: it prints `0x1.999999999999ap-4` in both compilers, with and without the `-mfpmath=387` flag. – anol Mar 04 '15 at 10:33
  • @CuriosGuy Note that the style that you used, obtained with inline backticks `\``, is intended for **code**. OTOH there is no recommendation for program output, so that's probably acceptable here (but do not use `\`` for emphasis). – Pascal Cuoq Feb 15 '17 at 09:18
  • Sir, In my program , it says cannot find FLT_EVAL_METHOD , why is that? – CuriosGuy Feb 19 '17 at 07:37
  • @CuriosGuy `FLT_EVAL_METHOD` must be defined by the header `float.h` of a C99-compliant compiler. Maybe your C compiler does not support C99? – Pascal Cuoq Feb 21 '17 at 20:20
1

Ignoring the printf problem which Pascal Cuoq addressed, I think GCC is correct here: according to the C99 standard, FLT_EVAL_METHOD == 2 should

evaluate all operations and constants to the range and precision of the long double type.

So, in this case, both 0.1 and 1.0 / ten are being evaluated to an extended precision approximation of 1/10.

I'm not sure what Clang is doing, though this question might provide some help.

Community
  • 1
  • 1
Simon Byrne
  • 7,694
  • 1
  • 26
  • 50