xref: /third_party/musl/src/math/fmodl.c (revision 570af302)
1570af302Sopenharmony_ci#include "libm.h"
2570af302Sopenharmony_ci
3570af302Sopenharmony_ci#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
4570af302Sopenharmony_cilong double fmodl(long double x, long double y)
5570af302Sopenharmony_ci{
6570af302Sopenharmony_ci	return fmod(x, y);
7570af302Sopenharmony_ci}
8570af302Sopenharmony_ci#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
9570af302Sopenharmony_cilong double fmodl(long double x, long double y)
10570af302Sopenharmony_ci{
11570af302Sopenharmony_ci	union ldshape ux = {x}, uy = {y};
12570af302Sopenharmony_ci	int ex = ux.i.se & 0x7fff;
13570af302Sopenharmony_ci	int ey = uy.i.se & 0x7fff;
14570af302Sopenharmony_ci	int sx = ux.i.se & 0x8000;
15570af302Sopenharmony_ci
16570af302Sopenharmony_ci	if (y == 0 || isnan(y) || ex == 0x7fff)
17570af302Sopenharmony_ci		return (x*y)/(x*y);
18570af302Sopenharmony_ci	ux.i.se = ex;
19570af302Sopenharmony_ci	uy.i.se = ey;
20570af302Sopenharmony_ci	if (ux.f <= uy.f) {
21570af302Sopenharmony_ci		if (ux.f == uy.f)
22570af302Sopenharmony_ci			return 0*x;
23570af302Sopenharmony_ci		return x;
24570af302Sopenharmony_ci	}
25570af302Sopenharmony_ci
26570af302Sopenharmony_ci	/* normalize x and y */
27570af302Sopenharmony_ci	if (!ex) {
28570af302Sopenharmony_ci		ux.f *= 0x1p120f;
29570af302Sopenharmony_ci		ex = ux.i.se - 120;
30570af302Sopenharmony_ci	}
31570af302Sopenharmony_ci	if (!ey) {
32570af302Sopenharmony_ci		uy.f *= 0x1p120f;
33570af302Sopenharmony_ci		ey = uy.i.se - 120;
34570af302Sopenharmony_ci	}
35570af302Sopenharmony_ci
36570af302Sopenharmony_ci	/* x mod y */
37570af302Sopenharmony_ci#if LDBL_MANT_DIG == 64
38570af302Sopenharmony_ci	uint64_t i, mx, my;
39570af302Sopenharmony_ci	mx = ux.i.m;
40570af302Sopenharmony_ci	my = uy.i.m;
41570af302Sopenharmony_ci	for (; ex > ey; ex--) {
42570af302Sopenharmony_ci		i = mx - my;
43570af302Sopenharmony_ci		if (mx >= my) {
44570af302Sopenharmony_ci			if (i == 0)
45570af302Sopenharmony_ci				return 0*x;
46570af302Sopenharmony_ci			mx = 2*i;
47570af302Sopenharmony_ci		} else if (2*mx < mx) {
48570af302Sopenharmony_ci			mx = 2*mx - my;
49570af302Sopenharmony_ci		} else {
50570af302Sopenharmony_ci			mx = 2*mx;
51570af302Sopenharmony_ci		}
52570af302Sopenharmony_ci	}
53570af302Sopenharmony_ci	i = mx - my;
54570af302Sopenharmony_ci	if (mx >= my) {
55570af302Sopenharmony_ci		if (i == 0)
56570af302Sopenharmony_ci			return 0*x;
57570af302Sopenharmony_ci		mx = i;
58570af302Sopenharmony_ci	}
59570af302Sopenharmony_ci	for (; mx >> 63 == 0; mx *= 2, ex--);
60570af302Sopenharmony_ci	ux.i.m = mx;
61570af302Sopenharmony_ci#elif LDBL_MANT_DIG == 113
62570af302Sopenharmony_ci	uint64_t hi, lo, xhi, xlo, yhi, ylo;
63570af302Sopenharmony_ci	xhi = (ux.i2.hi & -1ULL>>16) | 1ULL<<48;
64570af302Sopenharmony_ci	yhi = (uy.i2.hi & -1ULL>>16) | 1ULL<<48;
65570af302Sopenharmony_ci	xlo = ux.i2.lo;
66570af302Sopenharmony_ci	ylo = uy.i2.lo;
67570af302Sopenharmony_ci	for (; ex > ey; ex--) {
68570af302Sopenharmony_ci		hi = xhi - yhi;
69570af302Sopenharmony_ci		lo = xlo - ylo;
70570af302Sopenharmony_ci		if (xlo < ylo)
71570af302Sopenharmony_ci			hi -= 1;
72570af302Sopenharmony_ci		if (hi >> 63 == 0) {
73570af302Sopenharmony_ci			if ((hi|lo) == 0)
74570af302Sopenharmony_ci				return 0*x;
75570af302Sopenharmony_ci			xhi = 2*hi + (lo>>63);
76570af302Sopenharmony_ci			xlo = 2*lo;
77570af302Sopenharmony_ci		} else {
78570af302Sopenharmony_ci			xhi = 2*xhi + (xlo>>63);
79570af302Sopenharmony_ci			xlo = 2*xlo;
80570af302Sopenharmony_ci		}
81570af302Sopenharmony_ci	}
82570af302Sopenharmony_ci	hi = xhi - yhi;
83570af302Sopenharmony_ci	lo = xlo - ylo;
84570af302Sopenharmony_ci	if (xlo < ylo)
85570af302Sopenharmony_ci		hi -= 1;
86570af302Sopenharmony_ci	if (hi >> 63 == 0) {
87570af302Sopenharmony_ci		if ((hi|lo) == 0)
88570af302Sopenharmony_ci			return 0*x;
89570af302Sopenharmony_ci		xhi = hi;
90570af302Sopenharmony_ci		xlo = lo;
91570af302Sopenharmony_ci	}
92570af302Sopenharmony_ci	for (; xhi >> 48 == 0; xhi = 2*xhi + (xlo>>63), xlo = 2*xlo, ex--);
93570af302Sopenharmony_ci	ux.i2.hi = xhi;
94570af302Sopenharmony_ci	ux.i2.lo = xlo;
95570af302Sopenharmony_ci#endif
96570af302Sopenharmony_ci
97570af302Sopenharmony_ci	/* scale result */
98570af302Sopenharmony_ci	if (ex <= 0) {
99570af302Sopenharmony_ci		ux.i.se = (ex+120)|sx;
100570af302Sopenharmony_ci		ux.f *= 0x1p-120f;
101570af302Sopenharmony_ci	} else
102570af302Sopenharmony_ci		ux.i.se = ex|sx;
103570af302Sopenharmony_ci	return ux.f;
104570af302Sopenharmony_ci}
105570af302Sopenharmony_ci#endif
106