#ifdef HAVE_CONFIG_H #include #endif #include /* * The IEEE remainder x - n*y with n = x/y rounded to nearest, ties to * even (C23 7.12.10.2), plus the signed low bits of the integer quotient * n stored through quo (C23 7.12.10.3), all three precisions. * * The structure mirrors the host glibc remainder/remquo implementation * (glibc 2.44, measured byte-for-byte), because remquo's quotient is not * the full n: glibc tracks only the three lowest quotient bits and still * returns a remainder in the exact IEEE rounding, so the two functions * share one reduce-then-round shape. x is first reduced by fmod(x, 8y) * whenever |y| is small enough for 8y to be finite, which makes |x| < 8y * and therefore bounds x/y below 8; the tracked quotient cquo collects * the one 4y subtraction, the one 2y subtraction and the final rounding's * up-to-two y subtractions, which together hold the three lowest bits of * n. The final compare against y/2 (using doubled compares when y is so * small that halving could lose precision) rounds x to the nearest * multiple of y, ties to even via the natural 0.5 boundary, and each * subtraction that fires is exact (Sterbenz), so the returned remainder * is exact. * * The x86-64 host collapses the remainder and quotient signs the same * way the code below does: the magnitude result is computed from |x| and * |y| and then negated when x < 0, and *quo carries the sign of x/y. * Domain errors -- |y| == 0, x not finite, y a NaN -- return a NaN (the * classic indefinite pattern) and leave *quo untouched, as glibc does. * Like glibc, remquo never touches errno. */ /* The double format: bit 63 the sign, bits 62..52 the exponent biased by * 1023, bits 51..0 the fraction. */ static double remquo_d(double x, double y, int *quo) { const unsigned long long dbl_inf = 0x7FF0000000000000ULL; unsigned long long xw; unsigned long long yw; unsigned long long hx; unsigned long long hy; unsigned long long sx; unsigned long long sy; double ax; double ay; double yh; int cquo; int qneg; __builtin_memcpy(&xw, &x, sizeof xw); __builtin_memcpy(&yw, &y, sizeof yw); sx = xw >> 63; sy = yw >> 63; qneg = (int)(sx ^ sy); hx = xw & 0x7FFFFFFFFFFFFFFFULL; hy = yw & 0x7FFFFFFFFFFFFFFFULL; if (hy == 0 || hx >= dbl_inf || hy > dbl_inf) { xw = 0xFFF8000000000000ULL; __builtin_memcpy(&x, &xw, sizeof xw); return x; } if (hx == hy) { *quo = qneg ? -1 : 1; return x * 0.0; } ax = fabs(x); ay = fabs(y); if (hy <= 0x7FBFFFFFFFFFFFFFULL) { ax = fmod(ax, 8.0 * ay); /* now ax < 8 * ay */ } cquo = 0; if (hy <= 0x7FCFFFFFFFFFFFFFULL && ax >= 4.0 * ay) { ax -= 4.0 * ay; cquo += 4; } if (hy <= 0x7FDFFFFFFFFFFFFFULL && ax >= 2.0 * ay) { ax -= 2.0 * ay; cquo += 2; } if (hy < 0x0020000000000000ULL) { if (ax + ax > ay) { ax -= ay; cquo++; if (ax + ax >= ay) { ax -= ay; cquo++; } } } else { yh = 0.5 * ay; if (ax > yh) { ax -= ay; cquo++; if (ax >= yh) { ax -= ay; cquo++; } } } *quo = qneg ? -cquo : cquo; if (ax == 0.0) { ax = 0.0; } if (sx) { ax = -ax; } return ax; } /* The float format: bit 31 the sign, bits 30..23 the exponent biased by * 127, bits 22..0 the fraction. */ static float remquo_f(float x, float y, int *quo) { const unsigned int flt_inf = 0x7F800000U; unsigned int xw; unsigned int yw; unsigned int hx; unsigned int hy; unsigned int sx; unsigned int sy; float ax; float ay; float yh; int cquo; int qneg; __builtin_memcpy(&xw, &x, sizeof xw); __builtin_memcpy(&yw, &y, sizeof yw); sx = xw >> 31; sy = yw >> 31; qneg = (int)(sx ^ sy); hx = xw & 0x7FFFFFFFU; hy = yw & 0x7FFFFFFFU; if (hy == 0 || hx >= flt_inf || hy > flt_inf) { xw = 0xFFC00000U; __builtin_memcpy(&x, &xw, sizeof xw); return x; } if (hx == hy) { *quo = qneg ? -1 : 1; return x * 0.0f; } ax = fabsf(x); ay = fabsf(y); if (hy <= 0x7DFFFFFFU) { ax = fmodf(ax, 8.0f * ay); /* now ax < 8 * ay */ } cquo = 0; if (hy <= 0x7E7FFFFFU && ax >= 4.0f * ay) { ax -= 4.0f * ay; cquo += 4; } if (hy <= 0x7EFFFFFFU && ax >= 2.0f * ay) { ax -= 2.0f * ay; cquo += 2; } if (hy < 0x01000000U) { if (ax + ax > ay) { ax -= ay; cquo++; if (ax + ax >= ay) { ax -= ay; cquo++; } } } else { yh = 0.5f * ay; if (ax > yh) { ax -= ay; cquo++; if (ax >= yh) { ax -= ay; cquo++; } } } *quo = qneg ? -cquo : cquo; if (ax == 0.0f) { ax = 0.0f; } if (sx) { ax = -ax; } return ax; } /* * The x86 80-bit extended format: 64-bit significand m in bytes 0..7 (the * integer bit is explicit) and a sign/exponent word se in bytes 8..9 with * the sign in bit 15 and the exponent biased by 16383 in bits 14..0. */ struct remquo_ld_word { unsigned long long m; unsigned short se; }; static long double remquo_l(long double x, long double y, int *quo) { struct remquo_ld_word p; struct remquo_ld_word q; int ex; int ey; int qneg; int sx; int sy; long double ax; long double ay; long double yh; int cquo; __builtin_memcpy(&p, &x, sizeof p); __builtin_memcpy(&q, &y, sizeof q); sx = (int)(p.se >> 15); sy = (int)(q.se >> 15); qneg = sx ^ sy; ex = p.se & 0x7fff; ey = q.se & 0x7fff; if ((q.m == 0 && ey == 0) || ex == 0x7fff || (ey == 0x7fff && (q.m & 0x7FFFFFFFFFFFFFFFULL) != 0)) { p.m = 0xC000000000000000ULL; p.se = 0xFFFF; __builtin_memcpy(&x, &p, sizeof p); return x; } if (ex == ey && p.m == q.m) { *quo = qneg ? -1 : 1; return x * 0.0L; } ax = fabsl(x); ay = fabsl(y); if (ey <= 0x7ffb) { ax = fmodl(ax, 8.0L * ay); /* now ax < 8 * ay */ } cquo = 0; if (ey <= 0x7ffc && ax >= 4.0L * ay) { ax -= 4.0L * ay; cquo += 4; } if (ey <= 0x7ffd && ax >= 2.0L * ay) { ax -= 2.0L * ay; cquo += 2; } if (ey < 0x0002) { if (ax + ax > ay) { ax -= ay; cquo++; if (ax + ax >= ay) { ax -= ay; cquo++; } } } else { yh = 0.5L * ay; if (ax > yh) { ax -= ay; cquo++; if (ax >= yh) { ax -= ay; cquo++; } } } *quo = qneg ? -cquo : cquo; if (ax == 0.0L) { ax = 0.0L; } if (sx) { ax = -ax; } return ax; } /* * remquo for a float argument. */ float remquof(float x, float y, int *quo) // NOLINT(bugprone-easily-swappable-parameters) { return remquo_f(x, y, quo); } /* * remquo for a double argument. */ double remquo(double x, double y, int *quo) // NOLINT(bugprone-easily-swappable-parameters) { return remquo_d(x, y, quo); } /* * remquo for a long double argument. */ long double remquol(long double x, long double y, int *quo) // NOLINT(bugprone-easily-swappable-parameters) { return remquo_l(x, y, quo); }