NAN 用于算术/比较运算:编译器之间的不同结果wrt 引发浮点异常:如何解释?

如何解决NAN 用于算术/比较运算:编译器之间的不同结果wrt 引发浮点异常:如何解释?

上下文:对于以下示例代码,不同的编译器在引发浮点异常时会产生不同的结果。

问题:如何解释差异?

示例代码 (t125.c):

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

#if _MSC_VER && ! __clang__
#pragma fenv_access (on)
#else
#pragma STDC FENV_ACCESS ON
#endif

void show_fe_exceptions(void)
{
    printf("exceptions raised:");
    if (fetestexcept(FE_DIVBYZERO))     printf(" FE_DIVBYZERO");
    if (fetestexcept(FE_INEXACT))       printf(" FE_INEXACT");
    if (fetestexcept(FE_INVALID))       printf(" FE_INVALID");
    if (fetestexcept(FE_OVERFLOW))      printf(" FE_OVERFLOW");
    if (fetestexcept(FE_UNDERFLOW))     printf(" FE_UNDERFLOW");
    if (fetestexcept(FE_ALL_EXCEPT)==0) printf(" none");
    printf("\n");
}

/*
 * based on:
 * https://github.com/google/flatbuffers/blob/4133a39df80546ff5269894a3961c7069fcd45d0/tests/test.cpp#L693
 */
#if TID == 32
_Bool is_quiet_nan(float v)
#elif TID == 64
_Bool is_quiet_nan(double v)
#else
#error "unkNown TID"
#endif
{
#if TID == 32
    uint32_t qnan_base = 0x7FC00000u;
    uint32_t u;
#elif TID == 64
    uint64_t qnan_base = 0x7FF8000000000000ul;
    uint64_t u;
#else
#error "unkNown TID"
#endif
    _Static_assert(sizeof(v) == sizeof(u),"unexpected");
    memcpy(&u,&v,sizeof(v));
    return ((u & qnan_base) == qnan_base);
}

#if TID == 32
float xfma(float x,float y,float z) { return fmaf(x,y,z); }
float xsqrt(float x) { return sqrtf(x); }
#elif TID == 64
double xfma(double x,double y,double z) { return fma(x,z); }
double xsqrt(double x) { return sqrt(x); }
#else
#error "unkNown TID"
#endif

int main(void)
{
    volatile _Bool b;
#if TID == 32
    QUAL float f = NAN;
#elif TID == 64
    QUAL double f = NAN;
#else
#error "unkNown TID"
#endif

    printf("f is                %f\n",f);
    printf("f is quiet nan      %d\n",is_quiet_nan(f));
    printf("NAN is quiet nan    %d\n",is_quiet_nan(NAN));

#define TEST(expr)                      \
    feclearexcept(FE_ALL_EXCEPT);       \
    printf("%-25s => ",#expr);         \
    b = (expr) != 0;                    \
    show_fe_exceptions();

    printf("\n");
    printf("C11,5.2.4.2.2,p3:\n");
    printf("> A quiet NaN propagates through almost every arithmetic\n");
    printf("> operation without raising a floating-point exception; ...\n");
    TEST(f + f);
    TEST(f - f);
    TEST(f * f);
    TEST(f / f);
    TEST(xsqrt(f));
    TEST(xfma(f,f,f));

    TEST(NAN + NAN);
    TEST(NAN - NAN);
    TEST(NAN * NAN);
    TEST(NAN / NAN);
    TEST(xsqrt(NAN));
    TEST(xfma(NAN,NAN,NAN));

    printf("\n");
    printf("IEEE 754,5.11,p4:\n");
    printf("> Programs that explicitly take account of the possibility of\n");
    printf("> quiet NaN operands may use the unordered-quiet predicates in\n");
    printf("> Table 5.3 which do not signal such an invalid operation exception.\n");
    TEST(f == f);
    TEST(f != f);
    TEST(f >= f);
    TEST(f <= f);
    TEST(f >  f);
    TEST(f <  f);

    TEST(NAN == NAN);
    TEST(NAN != NAN);
    TEST(NAN >= NAN);
    TEST(NAN <= NAN);
    TEST(NAN >  NAN);
    TEST(NAN <  NAN);

    return b ? 1 : 0;
}

调用(注意:很多结果):

# clang
# const float (baseline)
$ clang t125.c -std=c11 -ffp-model=strict -Wall -Wextra -pedantic -DQUAL=const -O3 -DTID=32 && ./a.exe
t125.c:10:14: warning: pragma STDC FENV_ACCESS ON is not supported,ignoring pragma [-WunkNown-pragmas]
#pragma STDC FENV_ACCESS ON
             ^
1 warning generated.
f is                -nan(ind)
f is quiet nan      1
NAN is quiet nan    1

C11,p3:
> A quiet NaN propagates through almost every arithmetic
> operation without raising a floating-point exception; ...
f + f                     => exceptions raised: none
f - f                     => exceptions raised: none
f * f                     => exceptions raised: none
f / f                     => exceptions raised: none
xsqrt(f)                  => exceptions raised: none
xfma(f,f)             => exceptions raised: none
NAN + NAN                 => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN - NAN                 => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN * NAN                 => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN / NAN                 => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
xsqrt(NAN)                => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
xfma(NAN,NAN)       => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW

IEEE 754,p4:
> Programs that explicitly take account of the possibility of
> quiet NaN operands may use the unordered-quiet predicates in
> Table 5.3 which do not signal such an invalid operation exception.
f == f                    => exceptions raised: none
f != f                    => exceptions raised: none
f >= f                    => exceptions raised: FE_INVALID
f <= f                    => exceptions raised: FE_INVALID
f > f                     => exceptions raised: FE_INVALID
f < f                     => exceptions raised: FE_INVALID
NAN == NAN                => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN != NAN                => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN >= NAN                => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN <= NAN                => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN > NAN                 => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN < NAN                 => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW

$ get_cmd() { cmd="clang t125.c -std=c11 -ffp-model=strict -Wall -Wextra -pedantic \
-O3 -Wfatal-errors -DQUAL=$QUAL1 -DTID=$TID1 && ./a.exe > out1 && clang t125.c \
-std=c11 -ffp-model=strict -Wall -Wextra -pedantic -O3 -Wfatal-errors -DQUAL=$QUAL2\
-DTID=$TID2 && ./a.exe > out2 && diff out1 out2" ; }

# note: below we do not count `warning: pragma STDC FENV_ACCESS ON is not supported`

# const float vs const double
$ QUAL1=const TID1=32 QUAL2=const TID2=64 get_cmd ; eval $cmd
<nothing>

# const float vs volatile float
$ QUAL1=const TID1=32 QUAL2=volatile TID2=32 get_cmd ; eval $cmd
diff --git a/out1 b/out2
index 72a12bf..d87d79e 100644
--- a/out1
+++ b/out2
@@ -1,4 +1,4 @@
-f is                -nan(ind)
+f is                nan^M
 f is quiet nan      1
 NAN is quiet nan    1

# const float vs volatile double
$ QUAL1=const TID1=32 QUAL2=volatile TID2=64 get_cmd ; eval $cmd
diff --git a/out1 b/out2
index 72a12bf..d87d79e 100644
--- a/out1
+++ b/out2
@@ -1,4 @@
-f is                -nan(ind)
+f is                nan^M
 f is quiet nan      1
 NAN is quiet nan    1

# const double vs volatile float
$ QUAL1=const TID1=64 QUAL2=volatile TID2=32 get_cmd ; eval $cmd
diff --git a/out1 b/out2
index 72a12bf..d87d79e 100644
--- a/out1
+++ b/out2
@@ -1,4 @@
-f is                -nan(ind)
+f is                nan^M
 f is quiet nan      1
 NAN is quiet nan    1

# const double vs volatile double
$ QUAL1=const TID1=64 QUAL2=volatile TID2=64 get_cmd ; eval $cmd
diff --git a/out1 b/out2
index 72a12bf..d87d79e 100644
--- a/out1
+++ b/out2
@@ -1,4 @@
-f is                -nan(ind)
+f is                nan^M
 f is quiet nan      1
 NAN is quiet nan    1

# volatile float vs volatile double
$ QUAL1=volatile TID1=32 QUAL2=volatile TID2=64 get_cmd ; eval $cmd
<nothing>

# gcc
# const float (baseline)
$ gcc t125.c -std=c11 -Wall -Wextra -pedantic -O3 -DQUAL=const -DTID=32 && ./a.exe
t125.c:10: warning: ignoring ‘#pragma STDC FENV_ACCESS’ [-WunkNown-pragmas]
   10 | #pragma STDC FENV_ACCESS ON
      |
f is                nan
f is quiet nan      1
NAN is quiet nan    1

C11,f)             => exceptions raised: none
NAN + NAN                 => exceptions raised: none
NAN - NAN                 => exceptions raised: none
NAN * NAN                 => exceptions raised: none
NAN / NAN                 => exceptions raised: none
xsqrt(NAN)                => exceptions raised: none
xfma(NAN,NAN)       => exceptions raised: none

IEEE 754,p4:
> Programs that explicitly take account of the possibility of
> quiet NaN operands may use the unordered-quiet predicates in
> Table 5.3 which do not signal such an invalid operation exception.
f == f                    => exceptions raised: none
f != f                    => exceptions raised: none
f >= f                    => exceptions raised: none
f <= f                    => exceptions raised: none
f > f                     => exceptions raised: none
f < f                     => exceptions raised: none
NAN == NAN                => exceptions raised: none
NAN != NAN                => exceptions raised: none
NAN >= NAN                => exceptions raised: none
NAN <= NAN                => exceptions raised: none
NAN > NAN                 => exceptions raised: none
NAN < NAN                 => exceptions raised: none

$ get_cmd() { cmd="gcc t125.c -std=c11 -Wall -Wextra -pedantic -O3 -DQUAL=$QUAL1\
-DTID=$TID1 && ./a.exe > out1 && gcc t125.c -std=c11 -Wall -Wextra -pedantic -O3\
-DQUAL=$QUAL2 -DTID=$TID2 && ./a.exe > out2 && diff out1 out2" ; }

# note: below we do not count `warning: pragma STDC FENV_ACCESS ON is not supported`

# const float vs const double
$ QUAL1=const TID1=32 QUAL2=const TID2=64 get_cmd ; eval $cmd
<nothing>

# const float vs volatile float
$ QUAL1=const TID1=32 QUAL2=volatile TID2=32 get_cmd ; eval $cmd
diff --git a/out1 b/out2
index 9ef46f1..65f7158 100644
--- a/out1
+++ b/out2
@@ -24,10 +24,10 @@ IEEE 754,p4:
 > Table 5.3 which do not signal such an invalid operation exception.
 f == f                    => exceptions raised: none
 f != f                    => exceptions raised: none
-f >= f                    => exceptions raised: none
-f <= f                    => exceptions raised: none
-f > f                     => exceptions raised: none
-f < f                     => exceptions raised: none
+f >= f                    => exceptions raised: FE_INVALID
+f <= f                    => exceptions raised: FE_INVALID
+f > f                     => exceptions raised: FE_INVALID
+f < f                     => exceptions raised: FE_INVALID
 NAN == NAN                => exceptions raised: none
 NAN != NAN                => exceptions raised: none
 NAN >= NAN                => exceptions raised: none

# const float vs volatile double
$ QUAL1=const TID1=32 QUAL2=volatile TID2=64 get_cmd ; eval $cmd
diff --git a/out1 b/out2
index 9ef46f1..65f7158 100644
--- a/out1
+++ b/out2
@@ -24,p4:
 > Table 5.3 which do not signal such an invalid operation exception.
 f == f                    => exceptions raised: none
 f != f                    => exceptions raised: none
-f >= f                    => exceptions raised: none
-f <= f                    => exceptions raised: none
-f > f                     => exceptions raised: none
-f < f                     => exceptions raised: none
+f >= f                    => exceptions raised: FE_INVALID
+f <= f                    => exceptions raised: FE_INVALID
+f > f                     => exceptions raised: FE_INVALID
+f < f                     => exceptions raised: FE_INVALID
 NAN == NAN                => exceptions raised: none
 NAN != NAN                => exceptions raised: none
 NAN >= NAN                => exceptions raised: none

# const double vs volatile float
$ QUAL1=const TID1=64 QUAL2=volatile TID2=32 get_cmd ; eval $cmd
diff --git a/out1 b/out2
index 9ef46f1..65f7158 100644
--- a/out1
+++ b/out2
@@ -24,p4:
 > Table 5.3 which do not signal such an invalid operation exception.
 f == f                    => exceptions raised: none
 f != f                    => exceptions raised: none
-f >= f                    => exceptions raised: none
-f <= f                    => exceptions raised: none
-f > f                     => exceptions raised: none
-f < f                     => exceptions raised: none
+f >= f                    => exceptions raised: FE_INVALID
+f <= f                    => exceptions raised: FE_INVALID
+f > f                     => exceptions raised: FE_INVALID
+f < f                     => exceptions raised: FE_INVALID
 NAN == NAN                => exceptions raised: none
 NAN != NAN                => exceptions raised: none
 NAN >= NAN                => exceptions raised: none

# const double vs volatile double
$ QUAL1=const TID1=64 QUAL2=volatile TID2=64 get_cmd ; eval $cmd
diff --git a/out1 b/out2
index 9ef46f1..65f7158 100644
--- a/out1
+++ b/out2
@@ -24,p4:
 > Table 5.3 which do not signal such an invalid operation exception.
 f == f                    => exceptions raised: none
 f != f                    => exceptions raised: none
-f >= f                    => exceptions raised: none
-f <= f                    => exceptions raised: none
-f > f                     => exceptions raised: none
-f < f                     => exceptions raised: none
+f >= f                    => exceptions raised: FE_INVALID
+f <= f                    => exceptions raised: FE_INVALID
+f > f                     => exceptions raised: FE_INVALID
+f < f                     => exceptions raised: FE_INVALID
 NAN == NAN                => exceptions raised: none
 NAN != NAN                => exceptions raised: none
 NAN >= NAN                => exceptions raised: none

# volatile float vs volatile double
$ QUAL1=volatile TID1=32 QUAL2=volatile TID2=64 get_cmd ; eval $cmd
<nothing>

# cl
# const float (baseline)
$ cl t125.c /std:c11 /fp:strict /DQUAL=const /DTID=32 && t125
f is                nan
f is quiet nan      1
NAN is quiet nan    1

C11,p4:
> Programs that explicitly take account of the possibility of
> quiet NaN operands may use the unordered-quiet predicates in
> Table 5.3 which do not signal such an invalid operation exception.
f == f                    => exceptions raised: none
f != f                    => exceptions raised: none
f >= f                    => exceptions raised: FE_INVALID
f <= f                    => exceptions raised: FE_INVALID
f > f                     => exceptions raised: FE_INVALID
f < f                     => exceptions raised: FE_INVALID
NAN == NAN                => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN != NAN                => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN >= NAN                => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN <= NAN                => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN > NAN                 => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW
NAN < NAN                 => exceptions raised: FE_INEXACT FE_INVALID FE_OVERFLOW

$ get_cmd() { cmd="cl t125.c /std:c11 /fp:strict /DQUAL=$QUAL1 /DTID=$TID1\
&& ./t125.exe > out1 && cl t125.c /std:c11 /fp:strict /DQUAL=$QUAL2 /DTID=$TID2\
&& ./t125.exe > out2 && diff out1 out2" ; }

# const float vs const double
$ QUAL1=const TID1=32 QUAL2=const TID2=64 get_cmd ; eval $cmd
<nothing>

# const float vs volatile float
$ QUAL1=const TID1=32 QUAL2=volatile TID2=32 get_cmd ; eval $cmd
<nothing>

# const float vs volatile double
$ QUAL1=const TID1=32 QUAL2=volatile TID2=64 get_cmd ; eval $cmd
<nothing>

# const double vs volatile float
$ QUAL1=const TID1=64 QUAL2=volatile TID2=32 get_cmd ; eval $cmd
<nothing>

# const double vs volatile double
$ QUAL1=const TID1=64 QUAL2=volatile TID2=64 get_cmd ; eval $cmd
<nothing>

# volatile float vs volatile double
$ QUAL1=volatile TID1=32 QUAL2=volatile TID2=64 get_cmd ; eval $cmd
<nothing>

$ gcc --version
gcc (GCC) 10.2.0
copyright (C) 2020 Free Software Foundation,Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or fitness FOR A PARTIculaR PURPOSE.

clang --version
clang version 11.0.1
Target: x86_64-pc-windows-msvc
Thread model: posix

$ cl
Microsoft (R) C/C++ Optimizing Compiler Version 19.28.29913 for x64
copyright (C) Microsoft Corporation.  All rights reserved.

更新:事实证明,__STDC_IEC_559__ 值(如果已定义)取决于主机操作系统。例如,在 Windows 10(2004 版)上运行的 gcc 10.2.0(带有 -frounding-math -fsignaling-nans)和 clang 11.0.1(带有 -ffp-model=strict)都没有将 __STDC_IEC_559__ 定义为 1。因此,将主机操作系统从 Windows 10 切换到 Ubuntu 20.04 LTS,然后重做测试:https://pastebin.com/NJwcyVkm正文内容过多,必须使用外部内容托管服务)。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?