[英]MSVC: why “#pragma fenv_access (on)” causes “error C2099: initializer is not a constant”?
示例代碼(t50.c):
#pragma fenv_access (on)
float d = 0.0 + 0.0;
int main(void)
{
return 0;
}
調用:
$ cl t50.c /fp:strict
Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28611 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
t50.c
t50.c(2): error C2099: initializer is not a constant
問題:為什么#pragma fenv_access (on)
會導致error C2099: initializer is not a constant
? 原因/動機是什么?
是因為在#pragma fenv_access (on)
上不允許cl
執行常量折疊嗎?
從文檔中:
編譯器禁用浮點優化,因此您的代碼可以一致地訪問浮點環境。
...
受 fenv_access 約束的優化類型有:
全局公共子表達式消除
代碼運動
恆定折疊
將1.0 + 1.0
視為常量是常量折疊的一個示例。
盡管語言指定這是一個常量表達式,因此它可以用作 static 初始化程序,但編譯指示覆蓋了這種處理,因為它需要在運行時執行加法,以防它設置浮點標志。
fenv_access
pragma 文檔特別指出:
編譯器禁用浮點優化,因此您的代碼可以一致地訪問浮點環境。
因此,看起來禁用的優化之一是常量的折疊,這意味着:
float d = 0.0 + 0.0;
需要在運行時而不是編譯時計算。 所以編譯器是正確的,在這種情況下它不是一個常數。
如果#pragma fenv_access (on)
應該具有與#pragma STDC FENV_ACCESS ON
相同的效果,則編譯器在 C99 和 C11 標准方面被破壞。 但是,Microsoft 可能不關心 C 標准,並且 MSVC 可能對#pragma fenv_access (on)
的解釋與標准 pragma 不同。 該答案解決了#pragma STDC FENV_ACCESS ON
之后的標准是否允許。
在最高層,
float d = 0.0 + 0.0;
使用 static 存儲持續時間和初始化程序0.0 + 0.0
聲明 object d
。 表達式0.0 + 0.0
是一個常量表達式。 因此,在支持附件 F 浮點語義 (IEC 60559/IEEE 754) 的實現中,此處的0.0 + 0.0
將在翻譯時進行評估(好像),而不影響執行時的浮點環境。
C11,§F.8.4 常量表達式,¶1(添加了重點):
浮動類型的算術常量表達式,除了具有 static 或線程存儲持續時間的 object 的初始化程序中的一個之外,在執行期間被評估(好像)......
例子
#include <fenv.h> #pragma STDC FENV_ACCESS ON void f(void) { float w[] = { 0.0/0.0 }; // raise an exception static float x = 0.0/0.0; // does not raise an exception...
C11,§F.8.5 初始化,¶1:
…所有用於初始化具有 static 或線程存儲持續時間的對象的計算都在翻譯時完成(好像)。
例子
#include <fenv.h> #pragma STDC FENV_ACCESS ON void f(void) { float u[] = { 1.1e75 }; // raises exceptions static float v = 1.1e75; // does not raise exceptions...
這些示例表明,即使 FENV_ACCESS 已打開,具有 static 存儲持續時間的對象的初始化程序中也允許涉及浮點運算的常量表達式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.