-
Notifications
You must be signed in to change notification settings - Fork 136
FMA based division
Naoki Shibata edited this page May 11, 2020
·
4 revisions
Below is a source code for FMA-based division algorithm with integer reciprocal estimation. This implementation gives mostly correctly-rounded results, but not perfect.
#include <math.h>
static double mulsign(double x, double y) {
union {
double f;
uint64_t ul;
} cnvx, cnvy;
cnvx.f = x;
cnvy.f = y;
cnvy.ul &= 0x8000000000000000ULL;
cnvx.ul ^= cnvy.ul;
return cnvx.f;
}
static inline double divide(double x, double y) {
union {
double f;
uint64_t ul;
} cnvx, cnvy;
// Adjust the exponents of arguments
cnvy.f = y;
cnvy.ul &= 0x7fc0000000000000ULL;
cnvx.f = x;
cnvx.ul &= 0x7fc0000000000000ULL;
cnvx.ul = 0x7fd0000000000000ULL - ((cnvy.ul + cnvx.ul) >> 1);
y *= cnvx.f;
x *= cnvx.f;
//
cnvy.f = y;
int o = (cnvy.ul & 0x7fc0000000000000ULL) >= 0x7fc0000000000000ULL;
// Reciprocal estimation
int32_t i = 0x7fff & (int32_t)(cnvy.ul >> (52-15));
cnvy.ul = (uint64_t)0x7fe0000000000000LL - cnvy.ul;
int d;
d = (8432 * i - 763081686) >> 14;
d = (d * i + 982096282) >> 15;
d = (d * i - 2075152);
cnvy.ul = o ? 0 : (cnvy.ul - ((uint64_t)d << (52 - 23 - 7)));
// Newton-Raphson
double t = cnvy.f;
t = fma(t, fma(t, -y, 1), t);
t = fma(t, fma(t, -y, 1), t);
t = fma(t, fma(t, -y, 1), t);
// Reconstruction
double u = x * t;
u = fma(t, fma(u, -y, x), u);
// Fixup
if (isnan(u)) u = mulsign(mulsign(INFINITY, x), y);
if (isinf(y)) u = mulsign(mulsign(0 , x), y);
if (y == 0 && x == 0) u = NAN;
if (isnan(x)) u = x;
if (isnan(y)) u = y;
//
return u;
}
The following code is for reciprocal approximation.
float aprxrec(float x) {
union {
float f;
uint32_t ul;
} u;
u.f = x;
int32_t i = u.ul & 0x7fffff;
u.ul = 0x7f000000 - u.ul;
i >>= 8;
int d;
d = (8432 * i - 763081686) >> 14;
d = (d * i + 982096282) >> 15;
d = (d * i - 2075152) >> 7;
u.ul -= d;
return u.f;
}