summaryrefslogtreecommitdiff
path: root/src/math/fmaf.c
blob: 0bc35ec8eccc0deacd558c7e0659d2573f14067b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <math.h>
#include <stdint.h>

float fmaf(float x, float y, float z)
{
	double xy = (double)x * y;
	union {double r; uint64_t i;} u = {xy + z};
	int e = u.i>>52 & 0x7ff;
	/* covers |r| > 0x1p-126 halfway cases (may round incorrectly) */
	int halfway = (u.i & 0x1fffffff) == 0x10000000;
	/* covers tiny inexact (may miss uflow) and tiny halfway cases */
	int tiny = e <= 0x3ff-126 && e >= 0x3ff-149;
	if (!halfway && !tiny)
		/* common case, optimization only */
		return (float)u.r;
	if (e != 0x7ff) {
		/* r+t == x*y+z exactly in nearest rounding, otherwise
		 * rounding does not affect t!=0, t<0 and inexact flag. */
		int s = u.i >> 63;
		double t = s == (xy < z) ? xy - u.r + z : z - u.r + xy;
		if (t) {
			/* adjust r toward r+t if r%2==0 (round to odd).
			 * may be needed if r is halfway or exact float,
			 * wrong if r becomes halfway or exact float. */
			u.i -= s ^ (t<0);
			u.i |= 1;
		}
	}
	return (float)u.r;
}