C99:复数的虚部可以是负零

时间:2011-05-04 14:33:21

标签: c gcc c99 complex-numbers

是否可以在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/

中找到任何相关内容

5 个答案:

答案 0 :(得分:3)

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

b = 0.0 + (__extension__ -0.0iF)

根据G.5.2中的规则评估为(double)0.0 + (double _Imaginary)(-0.0i),并产生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版本实现了{C}的完全符合的_Imaginary类型;当您指定-std=c99时,对_Imaginary的支持将被关闭,您将依赖于符合上述情况的符合_Complex的实施方式。但这只是猜测;如果你真的很好奇,我会鼓励你提交一个bug,看看维护者说的是什么。实际上,无论如何,我会鼓励你提交一个bug。 始终提交错误

答案 1 :(得分:3)

_Imaginary_I * -0.0是否比(__extension__ -0.0iF)效果更好?

即将推出的C1x标准将包含CMPLX个宏,这些宏“就像实现支持的虚构类型和定义一样: #define CMPLX(x, y) ((double complex)((double)(x) + _Imaginary_I * (double)(y)))“。

N1570,§7.3.9.3。

答案 2 :(得分:1)

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

<强>附录

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

<强>撤消

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

答案 3 :(得分:1)

使用

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

on 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)

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

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

的内容
float _Complex c2 = -(0.0 - (__extension__ 0.0iF));

另见PR 24581

答案 4 :(得分:0)

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

  

J.1未指明的行为

     
      
  1. 以下未说明:
      [...]
       - 当存储在对象中时,负零是否变为正常零(6.2.6.2)。
  2.   

这将使您想要的更复杂。