【问题标题】:Why exactly using of a floating-point arithmetic in an integer constant expression is invalid?为什么在整数常量表达式中精确使用浮点运算是无效的?
【发布时间】:2022-01-14 19:52:52
【问题描述】:

在 C11(及更高版本)中,整数常量表达式只能具有以下操作数:

作为类型转换的直接操作数的浮动常量

以下代码:

int a[ A > B ? 16 : 32 ];

AB 是浮动常量时在C 中无效:

$ echo '#include "t576.h"' | clang -std=c11 -pedantic -Wall -Wextra -DA=1.0 -DB=2.0 -c -xc -
In file included from <stdin>:1:
./t576.h:1:5: warning: size of static array must be an integer constant expression [-Wpedantic]

但在 C++ 中有效:

$ echo '#include "t576.h"' | clang++ -std=c++11 -pedantic -Wall -Wextra -DA=1.0 -DB=2.0 -c -xc++ -
<nothing>

此要求的起源/基本原理是什么?

额外问题:在未来的 C 标准修订版中删除此要求是否有用?

【问题讨论】:

  • 浮点值AB 是“强制转换的直接操作数”吗?不会的。
  • 您究竟是如何定义 A 和 B 的?我在 C11 中将其测试为 const float A = 0.6f; const float B = 0.65f; int a[ A &gt; B ? 16 : 32 ];,它的行为符合预期。
  • @martorad:在 C 语言中,const 限定符不会神奇地将变量更改为编译时常量。只有#define 可用于定义符号常量。换句话说,您正在定义一个 VLA...
  • @SergeBallesta,确实,那是我的错。也就是说,使用#define 初始化 A 和 B 也适用于 C11。不过,就像您提到的那样,在不知道 OP 的定义的情况下,我们只能猜测。
  • @LanguageLawyer 这归结为浮点常量还是变量?

标签: c floating-point language-lawyer c11 constant-expression


【解决方案1】:

此要求的起源/基本原理是什么?

这意味着 C 编译器不需要能够在编译器内执行浮点运算。当针对不同于编译器宿主平台的目标平台进行编译时,复制目标浮点运算的确切行为可能需要大量工作。 (在浮点运算广泛采用 IEEE 754 标准之前尤其如此。)

要在 C 程序中实现浮点语义,编译器只需能够将源代码中的常量转换为目标浮点格式并获取它们的整数部分(在强制转换中)。它不必能够对它们执行一般的算术运算。如果没有这个要求,编译器将不得不重现目标平台的浮点算术运算。因此,如果程序使用浮点运算,编译器可以通过生成指令来执行运算;它不必自己做算术。对于算术常量表达式也是如此,它可以用作初始化器:C 标准并不严格要求编译器计算初始化器的值。它可以在程序开始运行时(用于静态对象的初始化)或需要时(用于自动对象的初始化)生成计算值的指令。

相比之下,整数常量表达式可以用在编译器需要值的地方,例如位域的宽度。所以编译器必须能够自己计算值。如果需要能够进行浮点运算来获取值,这会给编写一些编译器增加相当大的负担。

额外问题:在未来的 C 标准修订版中删除此要求是否有用?

删除它将为 C 程序编写者提供一些使用额外常量表达式的机会,并要求 C 编译器编写者做更多的工作。这些东西的价值是主观的。

【讨论】:

  • 浮点并不是 C 语言的可选特性。甚至对于独立的实现也不行。如果交叉编译器不能执行浮点计算,那将是一个非常奇怪的系统,但目标是。
  • @Lundin:问题不在于本身编译器可能根本无法执行浮点运算,尽管这是一种可能性,而是编译器可能无法轻松执行与目标相同的浮点计算,匹配浮点基数、精度、舍入和其他行为,因为目标浮点语义和编译器主机的语义可能不匹配浮点语义。
  • 回复:“不匹配”:一个example。然而,尽管存在潜在的不匹配,但初始化器中允许使用浮点运算。
  • 回复:“C 编译器不需要能够在编译器中执行浮点运算”:结果:代码 enum { x = (int)-1.0 }; 在 C 中无效(但在 C++ 中有效)。
【解决方案2】:

基本原理是常识:他们不想让用户声明一个包含 3.1415 个项目的数组 - 显然,数组大小需要是一个整数。

对于 C 中的许多运算符,只要存在浮点操作数,通常的算术转换就会将最终结果转换为浮点数。在?: 的情况下,特别是不会发生这种情况,因为结果是第二个或第三个操作数。此外,&gt; 运算符总是返回 int,因此它也并不真正适用于那里。

如果您不立即将浮点操作数转换为整数类型,如您引用的整数常量表达式的定义中所述,那么它将改为 算术常量表达式,这是一个更广泛的术语。

所以你可以这样做:

int a[ (int)1.0 > (int)2.0 ? 16 : 32 ]; // compliant

但你不能这样做:

int a[ 1.0 > 2.0 ? 16 : 32 ]; // not compliant

考虑int a[ (int)1.0 &gt; (int)2.0 ? 16.0 : 32 ];(也不符合)。在这里,条件总是评估为假。我们应该得到大小为 32,但由于 ?: 的特殊隐式转换规则,第 2 和第 3 个操作数在通常的算术转换中是平衡的,所以我们最终得到类型为 double32.0。如果这反过来会导致无法精确表示的浮点数,我们将获得浮点数组大小。

【讨论】:

  • 我认为这个答案错过了问题的重点,我将其解读为:“为什么 C 不允许在整数常量表达式中使用中间浮点步骤,而 C++ 证明它可能是可能有它们吗?” 我认为数组大小或条件运算符不是问题的核心,而只是用来演示它。
猜你喜欢
  • 1970-01-01
  • 2011-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-15
  • 1970-01-01
  • 2015-11-16
相关资源
最近更新 更多