f = NAN 可能导致引发浮点异常是否正常?
Is it considered normal that f = NAN may cause raising floating-point exceptions?
C2x(以及之前的):
The macro NAN
is defined if and only if the implementation supports quiet NaNs for the float
type. It expands to a constant expression of type float representing a quiet NaN.
示例代码 (t0a.c)
#include <stdio.h>
#include <math.h>
#include <fenv.h>
#if _MSC_VER && ! __clang__ && ! __INTEL_COMPILER
#pragma fenv_access (on)
#else
#pragma STDC FENV_ACCESS ON
#endif
void print_fe_excepts_raised(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");
}
int main(void)
{
float f;
feclearexcept(FE_ALL_EXCEPT);
f = NAN;
print_fe_excepts_raised();
(void)f;
return 0;
}
调用:
# msvc (version 19.29.30133 for x64)
$ cl t0a.c /std:c11 /Za /fp:strict && t0a.exe
exceptions raised FE_INEXACT FE_INVALID FE_OVERFLOW
# clang on Windows (version 13.0.0)
$ clang t0a.c -std=c11 -pedantic -Wall -Wextra -ffp-model=strict && ./a.exe
exceptions raised FE_INEXACT FE_INVALID FE_OVERFLOW
# gcc on Windows (version 11.2.0)
$ gcc t0a.c -std=c11 -pedantic -Wall -Wextra && ./a.exe
exceptions raised none
# gcc on Linux (version 11.2.0)
$ gcc t0a.c -std=c11 -pedantic -Wall -Wextra && ./a.out
exceptions raised none
# clang on Linux (version 13.0.0)
$ clang t0a.c -std=c11 -pedantic -Wall -Wextra -ffp-model=strict && ./a.out
exceptions raised none
对于 Windows 上的 msvc 和 clang:这是因为:
C:\Program Files (x86)\Windows Kits\Include.0.18362.0\ucrt\corecrt_math.h:94:9
#define NAN ((float)(INFINITY * 0.0F))
C:\Program Files (x86)\Windows Kits\Include.0.18362.0\ucrt\corecrt_math.h:90:9
#define INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF))
C:\Program Files (x86)\Windows Kits\Include.0.18362.0\ucrt\corecrt_math.h:87:13
#define _HUGE_ENUF 1e+300 // _HUGE_ENUF*_HUGE_ENUF must overflow
这里我们看到 NAN
“扩展为表示安静 NaN 的 float 类型的常量表达式”。这意味着 f = NAN
可能会导致浮点异常。但是,f = NAN
通常被视为“写入内存”。因此,人们可能会想:“写入内存如何导致引发浮点异常?”。
郑重声明,这...
The macro NAN
is defined if and only if the implementation supports quiet NaNs for the float
type. It expands to a constant expression of type float representing a quiet NaN.
... 是 C17 7.12/5,它可能在 C2x 中具有相同或相似的编号。
已更新
当在 Windows 上与 MSVC 或 Clang 一起使用时,您的测试程序导致引发 FE_INVALID
FP 异常 这一事实表明
- Microsoft 的 C 标准库和 运行时间环境
- MSVC 和 Clang 编译器和
- 您指定的选项
导致生成信号 NaN 并将其用作算术操作数。我同意这是一个意想不到的结果,可能表明这些组合未能完全符合这方面的 C 语言规范。
与生成的 NaN 是安静值还是信号值无关。那里的误解是 FE_INVALID
标志只会由于生成或操作信号 NaN 而被引发。事实并非如此。
一方面,IEEE-754 没有定义生成信令 NaN 的任何情况。所有定义的产生 NaN 的操作都会产生一个安静的 NaN,包括其中一个操作数是一个信号 NaN 的操作(因此 Windows 上的 MSVC 和 Clang 几乎肯定会产生一个安静的 NaN 作为 NAN
的值宏)。默认情况下,大多数将至少一个信号 NaN 作为操作数的操作会导致 FE_INVALID 标志被引发,但这不是引发该标志的通常原因。
相反,在默认的异常处理下,FE_INVALID
标志只是因为请求计算一个没有定义结果的操作而被引发,例如无穷大乘以 0。结果将是一个安静的 NaN。请注意,这不包括具有至少一个 NaN 操作数的操作,do 具有定义的结果:在许多情况下为安静的 NaN,用于比较的无序/假,以及少数其他结果案例。
关于上下文,重要的是要认识到,仅仅因为 NAN
扩展为常量表达式(在一致的 C 实现中)并不意味着该表达式的值是在编译时计算的。事实上,考虑到 MSVC 和 Clang 的严格 fp 模式的规范,我希望这些模式能够禁用大部分(如果不是全部的话)FP 表达式的编译时计算(或者至少传播 FP 状态标志,就好像计算是在 运行次)。
因此,提高 FE_INVALID
不一定是 f = NAN
中赋值的结果。如果(如在 Microsoft 的 C 标准库中)NAN
扩展为涉及算术运算的表达式,则应在计算该表达式时引发异常,尽管生成的 NaN 是安静的。至少在通过定义 __STDC_IEC_559__
功能测试宏来声称完全符合 IEC 60559 的实现中。
因此,虽然我不会对此提出异议
people may wonder: "how writing to memory may cause raising floating-point exceptions?".
,没有令人信服的证据表明已经观察到这种因果关系。
然而,在被计算的表达式中由 NAN
的特定外观表示的值具有某种物理表现形式。这在 FPU 寄存器中是合理的,并且将信号 NaN 从 FPU 寄存器存储到内存确实可能导致在某些体系结构上引发 FP 异常。
C2x(以及之前的):
The macro
NAN
is defined if and only if the implementation supports quiet NaNs for thefloat
type. It expands to a constant expression of type float representing a quiet NaN.
示例代码 (t0a.c)
#include <stdio.h>
#include <math.h>
#include <fenv.h>
#if _MSC_VER && ! __clang__ && ! __INTEL_COMPILER
#pragma fenv_access (on)
#else
#pragma STDC FENV_ACCESS ON
#endif
void print_fe_excepts_raised(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");
}
int main(void)
{
float f;
feclearexcept(FE_ALL_EXCEPT);
f = NAN;
print_fe_excepts_raised();
(void)f;
return 0;
}
调用:
# msvc (version 19.29.30133 for x64)
$ cl t0a.c /std:c11 /Za /fp:strict && t0a.exe
exceptions raised FE_INEXACT FE_INVALID FE_OVERFLOW
# clang on Windows (version 13.0.0)
$ clang t0a.c -std=c11 -pedantic -Wall -Wextra -ffp-model=strict && ./a.exe
exceptions raised FE_INEXACT FE_INVALID FE_OVERFLOW
# gcc on Windows (version 11.2.0)
$ gcc t0a.c -std=c11 -pedantic -Wall -Wextra && ./a.exe
exceptions raised none
# gcc on Linux (version 11.2.0)
$ gcc t0a.c -std=c11 -pedantic -Wall -Wextra && ./a.out
exceptions raised none
# clang on Linux (version 13.0.0)
$ clang t0a.c -std=c11 -pedantic -Wall -Wextra -ffp-model=strict && ./a.out
exceptions raised none
对于 Windows 上的 msvc 和 clang:这是因为:
C:\Program Files (x86)\Windows Kits\Include.0.18362.0\ucrt\corecrt_math.h:94:9
#define NAN ((float)(INFINITY * 0.0F))
C:\Program Files (x86)\Windows Kits\Include.0.18362.0\ucrt\corecrt_math.h:90:9
#define INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF))
C:\Program Files (x86)\Windows Kits\Include.0.18362.0\ucrt\corecrt_math.h:87:13
#define _HUGE_ENUF 1e+300 // _HUGE_ENUF*_HUGE_ENUF must overflow
这里我们看到 NAN
“扩展为表示安静 NaN 的 float 类型的常量表达式”。这意味着 f = NAN
可能会导致浮点异常。但是,f = NAN
通常被视为“写入内存”。因此,人们可能会想:“写入内存如何导致引发浮点异常?”。
郑重声明,这...
The macro
NAN
is defined if and only if the implementation supports quiet NaNs for thefloat
type. It expands to a constant expression of type float representing a quiet NaN.
... 是 C17 7.12/5,它可能在 C2x 中具有相同或相似的编号。
已更新
当在 Windows 上与 MSVC 或 Clang 一起使用时,您的测试程序导致引发 FE_INVALID
FP 异常 这一事实表明
- Microsoft 的 C 标准库和 运行时间环境
- MSVC 和 Clang 编译器和
- 您指定的选项
导致生成信号 NaN 并将其用作算术操作数。我同意这是一个意想不到的结果,可能表明这些组合未能完全符合这方面的 C 语言规范。
与生成的 NaN 是安静值还是信号值无关。那里的误解是 FE_INVALID
标志只会由于生成或操作信号 NaN 而被引发。事实并非如此。
一方面,IEEE-754 没有定义生成信令 NaN 的任何情况。所有定义的产生 NaN 的操作都会产生一个安静的 NaN,包括其中一个操作数是一个信号 NaN 的操作(因此 Windows 上的 MSVC 和 Clang 几乎肯定会产生一个安静的 NaN 作为 NAN
的值宏)。默认情况下,大多数将至少一个信号 NaN 作为操作数的操作会导致 FE_INVALID 标志被引发,但这不是引发该标志的通常原因。
相反,在默认的异常处理下,FE_INVALID
标志只是因为请求计算一个没有定义结果的操作而被引发,例如无穷大乘以 0。结果将是一个安静的 NaN。请注意,这不包括具有至少一个 NaN 操作数的操作,do 具有定义的结果:在许多情况下为安静的 NaN,用于比较的无序/假,以及少数其他结果案例。
关于上下文,重要的是要认识到,仅仅因为 NAN
扩展为常量表达式(在一致的 C 实现中)并不意味着该表达式的值是在编译时计算的。事实上,考虑到 MSVC 和 Clang 的严格 fp 模式的规范,我希望这些模式能够禁用大部分(如果不是全部的话)FP 表达式的编译时计算(或者至少传播 FP 状态标志,就好像计算是在 运行次)。
因此,提高 FE_INVALID
不一定是 f = NAN
中赋值的结果。如果(如在 Microsoft 的 C 标准库中)NAN
扩展为涉及算术运算的表达式,则应在计算该表达式时引发异常,尽管生成的 NaN 是安静的。至少在通过定义 __STDC_IEC_559__
功能测试宏来声称完全符合 IEC 60559 的实现中。
因此,虽然我不会对此提出异议
people may wonder: "how writing to memory may cause raising floating-point exceptions?".
,没有令人信服的证据表明已经观察到这种因果关系。
然而,在被计算的表达式中由 NAN
的特定外观表示的值具有某种物理表现形式。这在 FPU 寄存器中是合理的,并且将信号 NaN 从 FPU 寄存器存储到内存确实可能导致在某些体系结构上引发 FP 异常。