7

是否可以在 C99 复数浮点数的虚部存储负零?

我应该如何用带符号的虚部静态初始化复杂的常量?

我有一个小例子,但我不明白,为什么ac相同以及为什么-std=c99会改变结果。

$ cat zero1.c
int main() {
    float _Complex a;a = 0.0 + (__extension__ 0.0iF);
    float _Complex b;b = 0.0 + (__extension__ -0.0iF);
    float _Complex c;c = -0.0 + (__extension__ 0.0iF);
    float _Complex d;d = -0.0 + (__extension__ -0.0iF);
    printf("a= 0x%016llx\n", *(long long*)(&a));
    printf("b= 0x%016llx\n", *(long long*)(&b));
    printf("c= 0x%016llx\n", *(long long*)(&c));
    printf("d= 0x%016llx\n", *(long long*)(&d));
}

$ gcc-4.5.2 -w -std=c99 zero1.c ; ./a.out
a= 0x0000000000000000
b= 0x0000000000000000
c= 0x0000000000000000
d= 0x0000000080000000

$ gcc-4.5.2 -w zero1.c ; ./a.out
a= 0x0000000000000000
b= 0x8000000000000000
c= 0x0000000000000000
d= 0x8000000080000000

欢迎引用 C99-TC3 和 gcc 手册。

我在 C99 (n1256.pdf) 和http://www.knosof.co.uk/cbook/中找不到任何相关内容

4

5 回答 5

3

如果实现符合附录 G 并实现了_Imaginary类型,则表达式

b = 0.0 + (__extension__ -0.0iF)

(double)0.0 + (double _Imaginary)(-0.0i)根据 G.5.2 中的规则进行评估,并产生0.0 - 0.0i.

如果实现没有提供_Imaginary类型(这是允许的),或者不符合附录 G(也允许),那么这个表达式通常被评估为:

  (double _Complex)(0.0 + 0.0i) + (double _complex)(0.0 - 0.0i)
= (double _Complex)((0.0 + 0.0) + (0.0 - 0.0)i)

因为在 IEEE-754 默认舍入中0.0 - 0.0正零,所以符号位丢失。

故事的寓意:如果您关心零的符号,请不要在复杂的初始化程序中使用算术。由于您使用的是 GCC,因此您可以这样做:

__real__ c =  0.0f;
__imag__ c = -0.0f;

以我的经验,这至少可以追溯到 gcc-4.0 左右(可能更远)。

至于为什么该行为是由 触发的-std=c99,我的最佳猜测如下:您使用的 GCC 版本实现了一个_Imaginary不完全符合 C99 的类型;当您指定 时-std=c99,对 的支持_Imaginary将关闭,并且您将退回到一个符合_Complex我上面描述的工作的实现。然而,这只是一个猜测;如果你真的很好奇,我会鼓励你提交一个错误,看看维护者是怎么说的。实际上,我还是鼓励你提交一个错误。 总是提交一个错误

于 2011-05-04T17:32:53.210 回答
3

效果好_Imaginary_I * -0.0于?(__extension__ -0.0iF)

即将推出的 C1x 标准将包括CMPLX宏,“就好像实现支持想象的类型和定义是:
#define CMPLX(x, y) ((double complex)((double)(x) + _Imaginary_I * (double)(y)))”。</p>

参见N1570,§7.3.9.3。

于 2011-05-04T21:43:52.850 回答
1

它与 ISO C 标准规定的 IEEE 浮点行为有关,该标准对负零更为严格。以更原生的形式编译允许编译器优化,从而忽略更严格的规则,关于这些事情。

附录

我不记得细节了,但这在 ISO C99 标准的附录 F 中有深入讨论。PDF 可在:http ://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf 获得。

收回

对不起,我记错了。ISO C 标准显然没有规定任何关于负零的内容。这可能与 IEEE FP 操作的严格程度有关。

于 2011-05-04T14:45:18.947 回答
1

使用

gcc 版本 4.7.0 20110504(实验性)(GCC)

Target: x86_64-unknown-linux-gnu

无论有无-std=c99

a = 0x0000000000000000
b = 0x8000000000000000
c = 0x0000000000000000
d = 0x8000000080000000

所以我怀疑这是 4.5.2 中的一个错误,此后已修复。也许在 GCC bugzilla 和/或邮件列表中搜索会发现一些东西?

编辑剩下的谜团是 c 实部的符号在哪里?

EDIT2 c 实部的符号丢失,因为初始化程序包含一个加法,因此表达式被评估为 float _Complex 类型,因此

-0.0 + (__extension__ 0.0iF) = (-0.0, 0.0) + (0.0, 0.0) = (0.0, 0.0)

因为 -0.0 + 0.0 是 0.0,除非舍入模式是向负无穷大舍入。

因此,要生成文字 (-0, 0) 您需要类似

float _Complex c2 = -(0.0 - (__extension__ 0.0iF));

另见PR 24581

于 2011-05-04T15:07:37.950 回答
0

来自附件 J(可移植性问题):

J.1 未指明的行为

  1. 以下是未指定的:
    […]
    — 当存储在对象中时,[…] 负零是否变为正常零 (6.2.6.2)。

这将使您想要的东西变得更加复杂。

于 2011-05-05T18:00:32.763 回答