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 изключено не можем да разчитаме на 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]

Вижте също Ако pragma STDC FENV_ACCESS отсъства, това означава ли режим на закръгляване по подразбиране?, pragma STDC FENV_ACCESS ON не се поддържа

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

Не е толкова полезно, но не използвайте незадължителни функции на fenv.h или избягвайте избрани компилатори.

Може да съществува флаг за време на компилиране на gcc, който да помогне, но не знам за такъв.

Използвайте също #pragma STDC FENV_ACCESS ON @Eric Postpischil. Това може да не реши този проблем, но да предотврати свързани с него.

person chux - Reinstate Monica    schedule 13.09.2020
comment
И gcc, и clang не поддържат #pragma STDC FENV_ACCESS ON. gcc: warning: ignoring '#pragma STDC FENV_ACCESS', звън: warning: pragma STDC FENV_ACCESS ON is not supported. За clang изглежда, че има корекция: reviews.llvm.org/D69272. За cl: трябва да използвате #pragma fenv_access (on). - person pmor; 13.09.2020