许多计算科学较为著名的C ++库如征,Trilinos和deal.II使用标准的C ++模板库头对象std::complex<>
,表示复杂的浮点数。
在Jack Poulson 回答有关默认构造函数的问题时,他指出,“出于多种原因” 他std::complex
在Elemental中具有自己的实现。那是什么原因 这种方法的优点和缺点是什么?
许多计算科学较为著名的C ++库如征,Trilinos和deal.II使用标准的C ++模板库头对象std::complex<>
,表示复杂的浮点数。
在Jack Poulson 回答有关默认构造函数的问题时,他指出,“出于多种原因” 他std::complex
在Elemental中具有自己的实现。那是什么原因 这种方法的优点和缺点是什么?
Answers:
我相信这个讨论已经多次出现在PETSc清单上。我的主要原因是:
C ++标准指出,std :: complex仅为float,double和long double数据类型定义。因此,它不能用于其他数据类型,例如四精度。
该标准不能保证复杂算法的稳定性。
该标准不保证std :: complex中的数据存储为实数部分,后跟虚数部分。这对于与外部库(例如BLAS和LAPACK)的接口至关重要。所有主要实现都是如此,但我希望能够确保实现。
我更喜欢能够直接操纵实部和虚部。std :: complex使此操作不必要地困难。
我希望最终有一个更通用的版本,该版本仅要求数据类型为环,而不要求字段。这将包括高斯整数。
我std::complex<>
在程序中使用,并且必须与编译器标志和每种新编译器或编译器升级的解决方法作斗争。我将尝试按时间顺序叙述这些斗争:
std::norm
-ffast-math
std::arg
在某些配置下(与特定gcc版本的链接兼容性)被编译为非opt。问题经常浮出水面,因此std::arg
必须用代替atan2(imag(),real())
。但是在编写新代码时忘记这一点太容易了。std::complex
使用与内置C99复杂类型不同的调用约定(= ABI),并且对于较新的gcc版本使用内置的Fortran复杂类型。-ffast-math
浮点异常以意想不到的方式的处理编译标志交互。发生的情况是编译器将除法从循环中拉出,从而division by zero
在运行时导致异常。这些异常不会在循环内发生,因为相应的划分由于周围的逻辑而没有发生。那真的很糟糕,因为它是一个与使用浮点异常处理(使用不同的编译标志)的程序分开编译的库,并遇到了这些问题(相应的团队位于世界的相反部分,所以这个问题确实造成了严重的麻烦)。通过更仔细地手动进行编译器使用的优化来解决此问题。-ffast-math
编译标志。升级到较新的gcc版本后,性能下降了很多。我尚未对此问题进行详细调查,但我担心它与C99 Annex G有关。我必须承认,我对复数的乘积这个奇怪的定义完全感到困惑,甚至似乎存在这种说法的不同版本,并声称其他版本被误导了。我希望-fcx-limited-range
compile标志可以解决该问题,因为-ffast-math
对于此较新的gcc版本,似乎还存在另一个问题。-ffast-math
编译标志使行为NaN
完全不可预测(甚至isnan
会受影响)。唯一的解决方法似乎是避免NaN
在程序中发生任何事件,这破坏了存在的目的NaN
。现在您可能会问,std::complex
出于这些原因,我是否打算放弃内置的复杂类型。只要我使用C ++,我就将使用内置类型。万一C ++应该变得完全无法用于科学计算,我宁愿考虑改用一种语言,该语言应更多地关注与科学计算有关的问题。
z
是cv 类型的左值表达式,std::complex<T>
则reinterpret_cast<cv T(&)[2]>(z)
和reinterpret_cast<cv T(&)[2]>(z)[0]
将指定的实部z
,并且reinterpret_cast<cv T(&)[2]>(z)[1]
应指定的虚部z
。还解决了复数数组。