在clang ++中对复杂的<double>进行划分与g ++ </double>

时间:2014-05-07 13:34:40

标签: c++ clang complex-numbers

当我使用g ++(4.8.1或4.9.0)或clang ++(3.4)编译以下代码时,我得到不同的输出。

#include <iostream>
#include <complex>

int main() {
  std::complex<double> c = {1.e-162,0};
  std::cout << 1.0/c << std::endl;
  return 0;
}

克++:

(1e+162,0)

铛++:

(inf,-nan)

这是clang中的错误吗?

更新

感谢您的回答!我报告了这个错误:http://llvm.org/bugs/show_bug.cgi?id=19820

4 个答案:

答案 0 :(得分:5)

标准在[complex.numbers](26.4 / 3)中说:

  

如果函数的结果未在数学上定义或未在中   它的类型的可表示值的范围,行为是   未定义。

没有具体说明如何对复数进行划分。仅在[complex.member.ops]中说明:

complex<T>& operator/=(const complex<T>& rhs);
     

效果:将复数值rhs除以复数值*this   并将商存储在*this中。   返回:*this

[complex.ops]

template<class T> complex<T> operator/(const T& lhs, const complex<T>& rhs);
     

返回:complex<T>(lhs) /= rhs


由于1.e-162的倒数为1.e+162且此数字位于double的可表示值范围内,因此行为已明确定义。

因此gcc说得对,clang有一个bug。

答案 1 :(得分:4)

好的,我有这个疯狂的猜测,在clang中复杂的数字除法的实现就像在wiki:http://en.wikipedia.org/wiki/Complex_number#Multiplication_and_division上所描述的那样。

可以看出分母的格式为c^2 + d^2。因此,1.e-162平方实际上超出了double的{​​{1}} std::numeric_limits<double>::min()的IEE754可表示范围,并且我们有下溢。

2.22507e-308以某种方式解决了这个问题,但如果gcc执行简单的方格,则根据@ 40的标准引用,它会进入UB,并在执行后将其视为clang 0

我测试了1.e-162^2 + 0.0^2clang的数字,这个数字在平方时不会导致下溢。

gcc

结果很好:

#include <iostream>
#include <complex>

int main() {
  std::complex<double> c = {1.e-104,0};
  std::cout << 1.0/c << std::endl;
  return 0;
}

不确定这是不是一个错误。但我认为这是正在发生的事情。

附录:

如果一个人手动评估这些表达式,

luk32:~/projects/tests$ g++ --std=c++11 ./complex_div.cpp luk32:~/projects/tests$ ./a.out (1e+104,0) luk32:~/projects/tests$ clang++ --std=c++11 ./complex_div.cpp luk32:~/projects/tests$ ./a.out (1e+104,0) 也是一致的

我们得到:

(inf,-nan)

我对real = (ac+bd) / (o) - real part imag = (bc-ad) / (o) - imaginary part {a,b} = {1.0, 0.0} {c,d} = {1.e-104, 0.0} o is (c^2 + d^2) and we assume it is 0.0. real / o = (1.e-104 * 1.0 + 0.0 * 0.0) / o = 1/o = inf imag / o = (0.0 * 1.e-104 - 1.0 * 0.0) / o = -0.0 / o = -nan -0.0的标志并不是绝对确定,我不知道IEE754是否足以评估-nan。但一切似乎都是一致的。

答案 2 :(得分:2)

<强>更新

引用标准:

  

如果在评估表达式期间,结果不是   在数学上定义或不在可表示值的范围内   它的类型,行为未定义。 [注:大多数现有   C ++的实现忽略整数流。治疗师   零,使用零除数形成余数,并且全部浮动   点异常因机器而异,通常可通过a调整   库函数]。

引自 http://lists.freebsd.org/pipermail/freebsd-numerics/2014-March/000549.html

  

似乎clang开发人员选择了天真的复合体   除法算法。

     

...

     

我做了一些grepping。可能是划分算法   包含在文件中   src / contrib / llvm / tools / clang / lib / CodeGen / CGExprComplex.cpp里面   function ComplexExprEmitter :: EmitBinDiv?

     

如果你看一下代码,它看起来肯定是在生成代码   执行复杂的划分,它看起来确实如此   使用天真算法。

假设clang确实使用了幼稚的复数除法,表达式1.0 / c根据复杂除法的朴素实现来评估以下表达式 enter image description here

enter image description here

1.e-324超出双重范围。这是根据未定义行为的标准得出的结果。

同样在LLVM/Clang bug list进行搜索,似乎有很多关于复杂分割的问题。

因此,您的案例是个错误,您应该报告。

对于对如何实施复杂分工感兴趣的人,请查看

  1. http://ideone.com/bqFk8j
  2. A Robust Complex Division in Scilab.

答案 3 :(得分:1)

是。你应该报告一个错误。

复数1e-162 + 0i与实数1e-162相同。这完全落在双倍的范围内。该值的倒数应为1e + 162。任何其他值都代表算术库中的错误。

clang返回的值是错误的。这是一个错误。