| #include <cmath> |
| #include <complex> |
| |
| extern "C" { |
| void sladiv_(float* a, float* b, float* c, float* d, float* p, float* q); |
| void dladiv_(double* a, double* b, double* c, double* d, double* p, double* q); |
| std::complex<float> cladiv_(std::complex<float>* x, std::complex<float>* y); |
| std::complex<double> zladiv_(std::complex<double>* x, std::complex<double>* y); |
| } |
| |
| // This implementation is from pg. 21 |
| // http://www.open-std.org/jtc1/sc22/wg11/docs/n435.pdf and LLVM's libc++. |
| template <typename T> |
| void complex_divide(T a, T b, T c, T d, T& x, T& y) { |
| int ilogbw = 0; |
| const T logbw = std::logb(std::fmax(std::abs(c), std::abs(d))); |
| if (std::isfinite(logbw)) { |
| ilogbw = static_cast<int>(logbw); |
| c = scalbn(c, -ilogbw); |
| d = scalbn(d, -ilogbw); |
| } |
| const T denom = c * c + d * d; |
| |
| x = scalbn((a * c + b * d) / denom, -ilogbw); |
| y = scalbn((b * c - a * d) / denom, -ilogbw); |
| if (std::isnan(x) && std::isnan(y)) { |
| if ((denom == T(0)) && (!std::isnan(a) || !std::isnan(b))) { |
| x = std::copysign(std::numeric_limits<T>::infinity(), c) * a; |
| y = std::copysign(std::numeric_limits<T>::infinity(), c) * b; |
| } else if ((std::isinf(a) || std::isinf(b)) && std::isfinite(c) && |
| std::isfinite(d)) { |
| a = std::copysign(std::isinf(a) ? T(1) : T(0), a); |
| b = std::copysign(std::isinf(b) ? T(1) : T(0), b); |
| x = std::numeric_limits<T>::infinity() * (a * c + b * d); |
| y = std::numeric_limits<T>::infinity() * (b * c - a * d); |
| } else if (std::isinf(logbw) && logbw > T(0) && std::isfinite(a) && |
| std::isfinite(b)) { |
| c = std::copysign(std::isinf(c) ? T(1) : T(0), c); |
| d = std::copysign(std::isinf(d) ? T(1) : T(0), d); |
| x = T(0) * (a * c + b * d); |
| y = T(0) * (b * c - a * d); |
| } |
| } |
| } |
| |
| void sladiv_(float* a, float* b, float* c, float* d, float* p, float* q) { |
| complex_divide(*a, *b, *c, *d, *p, *q); |
| } |
| |
| void dladiv_(double* a, double* b, double* c, double* d, double* p, double* q) { |
| complex_divide(*a, *b, *c, *d, *p, *q); |
| } |
| |
| std::complex<float> cladiv_(std::complex<float>* x, std::complex<float>* y) { |
| float p, q; |
| complex_divide(x->real(), x->imag(), y->real(), y->imag(), p, q); |
| return std::complex<float>(p, q); |
| } |
| |
| std::complex<double> zladiv_(std::complex<double>* x, std::complex<double>* y) { |
| double p, q; |
| complex_divide(x->real(), x->imag(), y->real(), y->imag(), p, q); |
| return std::complex<double>(p, q); |
| } |