IEEE 754: sqrtf() с fesetround(): разные результаты между компиляторами: 0x42440a72 против 0x42440a73

#include <stdio.h>
#include <stdint.h>
#include <fenv.h>
#include <math.h>

int main()
{
    typedef union { uint32_t u; float f; } ufloat;
    ufloat val;
    float arg = 2401.999999;
    int r;
    r = fesetround(FE_DOWNWARD);
    val.f = sqrtf(arg);
    printf ("FE_DOWNWARD   %22.13a [0x%x] %d\n", val.f, val.u, r);
    r = fesetround(FE_TONEAREST);
    val.f = sqrtf(arg);
    printf ("FE_TONEAREST  %22.13a [0x%x] %d\n", val.f, val.u, r);
    r = fesetround(FE_TOWARDZERO);
    val.f = sqrtf(arg);
    printf ("FE_TOWARDZERO %22.13a [0x%x] %d\n", val.f, val.u, r);
    r = fesetround(FE_UPWARD);
    val.f = sqrtf(arg);
    printf ("FE_UPWARD     %22.13a [0x%x] %d\n", val.f, val.u, r);
    return 0;
}

Хост: Win10 x64.

Полученные результаты:

Дело 1.

$ clang t2.c -o t2.clang.exe && ./t2.clang.exe
FE_DOWNWARD     0x1.8814e60000000p+5 [0x42440a73] 0
FE_TONEAREST    0x1.8814e60000000p+5 [0x42440a73] 0
FE_TOWARDZERO   0x1.8814e60000000p+5 [0x42440a73] 0
FE_UPWARD       0x1.8814e60000000p+5 [0x42440a73] 0
 
$ clang --version
clang version 8.0.1 (tags/RELEASE_801/final)

Случай 2.

$ gcc t2.c -o t2.gcc.exe && ./t2.gcc.exe
FE_DOWNWARD     0x1.8814e60000000p+5 [0x42440a73] 0
FE_TONEAREST    0x1.8814e60000000p+5 [0x42440a73] 0
FE_TOWARDZERO   0x1.8814e60000000p+5 [0x42440a73] 0
FE_UPWARD       0x1.8814e60000000p+5 [0x42440a73] 0
 
$ gcc --version
gcc (GCC) 10.2.0

Случай 3.

cl t2.c && t2
Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28611 for x64
...
FE_DOWNWARD     0x1.8814e40000000p+5 [0x42440a72] 0
FE_TONEAREST    0x1.8814e60000000p+5 [0x42440a73] 0
FE_TOWARDZERO   0x1.8814e40000000p+5 [0x42440a72] 0
FE_UPWARD       0x1.8814e60000000p+5 [0x42440a73] 0

Случай 4.

cl t2.c && t2
Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28611 for x86
...
The system cannot execute the specified program.
Pop-up window appears: "Virus & thread protection: Windows Defender Antivirus found threats. Get details."

Случай 5.

wandbox.org: gcc HEAD 11.0.0 20200
$ gcc prog.c -Wall -Wextra -std=c99 "-lm"
FE_DOWNWARD     0x1.8814e40000000p+5 [0x42440a72] 0
FE_TONEAREST    0x1.8814e60000000p+5 [0x42440a73] 0
FE_TOWARDZERO   0x1.8814e40000000p+5 [0x42440a72] 0
FE_UPWARD       0x1.8814e60000000p+5 [0x42440a73] 0

Случай 6.

wandbox.org: clang HEAD 12.0.0
$ clang prog.c -Wall -Wextra -std=gnu11 "-lm"
FE_DOWNWARD     0x1.8814e40000000p+5 [0x42440a72] 0
FE_TONEAREST    0x1.8814e60000000p+5 [0x42440a73] 0
FE_TOWARDZERO   0x1.8814e40000000p+5 [0x42440a72] 0
FE_UPWARD       0x1.8814e60000000p+5 [0x42440a73] 0

Вопросы:

  1. Почему между компиляторами разные результаты (0x42440a72 и 0x42440a73)?
  2. Как получить одинаковые результаты между компиляторами?

person pmor    schedule 13.09.2020    source источник
comment
Вы можете и должны публиковать их, только не размещайте все в виде блока кода. Форматируйте заголовки отдельных тиражей как обычный текст!   -  person Antti Haapala    schedule 13.09.2020
comment
@AnttiHaapala, спасибо за подсказку (с использованием шаблона <pre>code</pre>)!   -  person pmor    schedule 13.09.2020
comment
Изменение режимов округления с плавающей запятой плохо поддерживается некоторыми компиляторами и библиотеками, но одна проблема в вашем исходном коде заключается в том, что стандарт C не определяет поведение, если вы изменяете режимы округления с выключенным FENV_ACCESS (C 2018 7.6.1 2). Вы можете включить его с помощью #pragma STDC FENV_ACCESS ON в компиляторах, которые его поддерживают.   -  person Eric Postpischil    schedule 13.09.2020
comment
Правильно ли я понимаю, что в случае FENV_ACCESS off мы не можем полагаться на возвращаемое значение fesetround() из-за UB? В приведенном выше примере fesetround() действительно [ложно] возвращает 0 (т. е. направление округления было успешно установлено), но из результатов мы видим, что направление округления не было задано.   -  person pmor    schedule 13.09.2020


Ответы (1)


Почему у компиляторов разные результаты (0x42440a72 и 0x42440a73)?

По крайней мере, для более старых версий gcc поддержка <fenv.h> не требуется.

См. доступ к среде с плавающей запятой в ‹fenv.h›, функция библиотеки, поддержка компилятора не требуется.

Когда я компилирую с GNU C11 (GCC) версии 9.3.0 (x86_64-pc-cygwin), ниже

#include <fenv.h>
#pragma STDC FENV_ACCESS ON

Я получаю следующее:

warning: ignoring #pragma STDC FENV_ACCESS [-Wunknown-pragmas]

См. также Если прагма STDC FENV_ACCESS отсутствует, означает ли это режим округления по умолчанию?, прагма STDC FENV_ACCESS ON не поддерживается

Как получить одинаковые результаты между компиляторами?

Не очень полезно, но не используйте дополнительные функции fenv.h и не избегайте избранных компиляторов.

Может существовать флаг времени компиляции gcc, но я не знаю о нем.

Также используйте #pragma STDC FENV_ACCESS ON @Eric Postpischil. Это может не решить эту проблему, но предотвратить сопутствующие.

person chux - Reinstate Monica    schedule 13.09.2020