blob: 381950f6801495b6d7828aa834a96e9689dce377 [file] [log] [blame]
#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);
}