2

我对 C 标准的解释有疑问,最新草案取自 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2454.pdf

标准评价

该标准定义pragma STD FENV_ACCESS并声明(7.6.1p2):

The FENV_ACCESS pragma provides a means to inform the implementation when a program might
access the floating-point environment to test floating-point status flags or run under non-default
floating-point control modes.

目前尚不清楚为什么这个 pragma 必须在非默认浮点控制模式下运行。是不是因为

  • 设置这些非默认模式需要写入控制模式寄存器,或者
  • 即使已经设置了非默认当前模式,编译指示总是必要的?

在标准的这一段后面,我们发现:

If part of a program tests floating-point status flags or establishes non-default floating-point
mode settings using any means other than the FENV_ROUND pragmas, but was translated with the
state for the FENV_ACCESS pragma "off", the behavior is undefined.

看起来测试当前模式而不改变它不是未定义的行为。但同一段的脚注指出:

In general, if the state of FENV_ACCESS is "off", the translator can assume that the flags are
not tested, and that default modes are in effect, except where specified otherwise by an
FENV_ROUND pragma.

问题

因此,如果没有指定编译指示 FENV_ACCESS,是否意味着默认舍入模式有效?

让我们假设 pragma FENV_ROUND 也不存在,并且编译器默认 FENV_ACCESS 是关闭的,这是向后兼容所必需的。

例子

考虑以下源代码:

#include <math.h>
float func_01(float x) {
    return nearbyint(x);
}

该功能nearbyint被描述为(7.12.9.3)使用当前舍入模式进行舍入。但是代码没有pragma FENV_ACCESS。这是否意味着当前的舍入模式可以被忽略并且与nearbyint相同roundeven

4

1 回答 1

1

C 草案 n2454

我根据当前的 C 标准 2018 编写了这个答案,但问题是关于即将发布的标准的草案。审稿后,有重大变化,此答案不适用。

值得注意的是,n2454 草案在 7.6.1 2 中指出:

... 如果程序的一部分测试浮点状态标志或使用除FENV_ROUND编译指示以外的任何方式建立非默认浮点模式设置,但被转换为FENV_ACCESS编译指示“关闭”的状态,则行为未定义......</ p>

值得注意的是,在“非默认浮点模式设置”之后出现的 C 2018 中的这段文本:

...或在非默认模式设置下运行,...</p>

C 2018 文本的意思是,如果使用FENV_ACCESSon 编译的代码设置非默认模式并设置使用FENV_ACCESSoff 编译的代码,则未定义行为,仅仅是因为使用FENV_ACCESSoff 编译的代码在非默认模式下运行。草案文本不包含此内容,这似乎暗示调用者可以更改模式并调用使用FENV_ACCESSoff 编译的代码,并且应该定义行为。这意味着使用 off 编译的代码FENV_ACCESS必须准备好在任何浮点模式下运行。

草案中的同一段也包含这个新案文:

(当执行从翻译为FENV_ACCESS“off”的程序部分转到翻译为FENV_ACCESS“on”的部分时,浮点状态标志的状态未指定,浮点控制模式具有其默认设置。)

考虑当例程 A 与FENV_ACCESSon 调用例程 B时会发生什么FENV_ACCESS。当 B 返回时,控制从程序的访问关闭部分传递到程序的访问打开部分。上面的句子说浮点控制模式处于默认设置。换句话说,从访问关闭例程返回必须将浮点模式更改为默认值。这似乎很奇怪。因此,我不准备更新此答案以很好地涵盖草稿。

C 2018 的答案

目前尚不清楚为什么这个 pragma 必须在非默认浮点控制模式下运行。

这是因为如果不知道浮点运算处于默认模式,编译器生成的代码可能(取决于 C 实现)可能不同。例如,当编译FENV_ACCESS设置为off的代码时,编译器可以将调用编译sin为对假定默认舍入的快速版本的调用。但如果FENV_ACCESS设置为on,它会将调用编译为测试舍入模式并使用正弦函数的相应实现的较慢版本。

因为onoff版本必须生成的代码不同,所以编译器必须知道FENV_ACCESSon还是off

因此,如果没有指定编译指示 FENV_ACCESS,是否意味着默认舍入模式有效?

不。如果FENV_ACCESS编译指示不存在,则编译器处于其默认状态,可能是onor off,这是实现定义的。

如果默认是off并且没有 pragma,那么,是的,默认舍入模式应该生效,这意味着,如果您的程序设计正确,那么任何没有FENV_ACCESSpragma 编译的代码都不会在非默认舍入中执行模式。这取决于程序设计者来确保。

该功能nearbyint被描述为(7.12.9.3)使用当前舍入模式进行舍入。但是代码没有pragma FENV_ACCESS。这是否意味着当前的舍入模式可以被忽略并且与nearbyint相同roundeven

如果FENV_ACCESS设置为off(默认或显式)的代码调用nearbyint,则编译器可以假定默认舍入模式有效,并且它可以调用nearbyint本身假定默认舍入模式的快速版本。

请注意,round-to-nearest-ties-to-even 压倒了默认的舍入模式,但除非附录 F 生效,否则 C 标准并未对此进行规定。

于 2020-03-17T11:50:13.037 回答