1/* Floating-point remainder function.
2 Copyright (C) 2023-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <libm-alias-finite.h>
20#include <libm-alias-float.h>
21#include <math-svid-compat.h>
22#include <math.h>
23#include "math_config.h"
24
25/* With x = mx * 2^ex and y = my * 2^ey (mx, my, ex, ey being integers), the
26 simplest implementation is:
27
28 mx * 2^ex == 2 * mx * 2^(ex - 1)
29
30 or
31
32 while (ex > ey)
33 {
34 mx *= 2;
35 --ex;
36 mx %= my;
37 }
38
39 With the mathematical equivalence of:
40
41 r == x % y == (x % (N * y)) % y
42
43 And with mx/my being mantissa of a single floating point number (which uses
44 less bits than the storage type), on each step the argument reduction can
45 be improved by 8 (which is the size of uint32_t minus MANTISSA_WIDTH plus
46 the implicit one bit):
47
48 mx * 2^ex == 2^8 * mx * 2^(ex - 8)
49
50 or
51
52 while (ex > ey)
53 {
54 mx << 8;
55 ex -= 8;
56 mx %= my;
57 }
58
59 Special cases:
60 - If x or y is a NaN, a NaN is returned.
61 - If x is an infinity, or y is zero, a NaN is returned and EDOM is set.
62 - If x is +0/-0, and y is not zero, +0/-0 is returned. */
63
64float
65__fmodf (float x, float y)
66{
67 uint32_t hx = asuint (f: x);
68 uint32_t hy = asuint (f: y);
69
70 uint32_t sx = hx & SIGN_MASK;
71 /* Get |x| and |y|. */
72 hx ^= sx;
73 hy &= ~SIGN_MASK;
74
75 if (__glibc_likely (hx < hy))
76 {
77 /* If y is a NaN, return a NaN. */
78 if (__glibc_unlikely (hy > EXPONENT_MASK))
79 return x * y;
80 return x;
81 }
82
83 int ex = hx >> MANTISSA_WIDTH;
84 int ey = hy >> MANTISSA_WIDTH;
85 int exp_diff = ex - ey;
86
87 /* Common case where exponents are close: |x/y| < 2^9, x not inf/NaN
88 and |x%y| not denormal. */
89 if (__glibc_likely (ey < (EXPONENT_MASK >> MANTISSA_WIDTH) - EXPONENT_WIDTH
90 && ey > MANTISSA_WIDTH
91 && exp_diff <= EXPONENT_WIDTH))
92 {
93 uint32_t mx = (hx << EXPONENT_WIDTH) | SIGN_MASK;
94 uint32_t my = (hy << EXPONENT_WIDTH) | SIGN_MASK;
95
96 mx %= (my >> exp_diff);
97
98 if (__glibc_unlikely (mx == 0))
99 return asfloat (i: sx);
100 int shift = __builtin_clz (mx);
101 ex -= shift + 1;
102 mx <<= shift;
103 mx = sx | (mx >> EXPONENT_WIDTH);
104 return asfloat (i: mx + ((uint32_t)ex << MANTISSA_WIDTH));
105 }
106
107 if (__glibc_unlikely (hy == 0 || hx >= EXPONENT_MASK))
108 {
109 /* If x is a NaN, return a NaN. */
110 if (hx > EXPONENT_MASK)
111 return x * y;
112
113 /* If x is an infinity or y is zero, return a NaN and set EDOM. */
114 return __math_edomf (x: (x * y) / (x * y));
115 }
116
117 /* Special case, both x and y are denormal. */
118 if (__glibc_unlikely (ex == 0))
119 return asfloat (i: sx | hx % hy);
120
121 /* Extract normalized mantissas - hx is not denormal and hy != 0. */
122 uint32_t mx = get_mantissa (x: hx) | (MANTISSA_MASK + 1);
123 uint32_t my = get_mantissa (x: hy) | (MANTISSA_MASK + 1);
124 int lead_zeros_my = EXPONENT_WIDTH;
125
126 ey--;
127 /* Special case for denormal y. */
128 if (__glibc_unlikely (ey < 0))
129 {
130 my = hy;
131 ey = 0;
132 exp_diff--;
133 lead_zeros_my = __builtin_clz (my);
134 }
135
136 int tail_zeros_my = __builtin_ctz (my);
137 int sides_zeroes = lead_zeros_my + tail_zeros_my;
138
139 int right_shift = exp_diff < tail_zeros_my ? exp_diff : tail_zeros_my;
140 my >>= right_shift;
141 exp_diff -= right_shift;
142 ey += right_shift;
143
144 int left_shift = exp_diff < EXPONENT_WIDTH ? exp_diff : EXPONENT_WIDTH;
145 mx <<= left_shift;
146 exp_diff -= left_shift;
147
148 mx %= my;
149
150 if (__glibc_unlikely (mx == 0))
151 return asfloat (i: sx);
152
153 if (exp_diff == 0)
154 return make_float (x: mx, ep: ey, s: sx);
155
156 /* Multiplication with the inverse is faster than repeated modulo. */
157 uint32_t inv_hy = UINT32_MAX / my;
158 while (exp_diff > sides_zeroes) {
159 exp_diff -= sides_zeroes;
160 uint32_t hd = (mx * inv_hy) >> (BIT_WIDTH - sides_zeroes);
161 mx <<= sides_zeroes;
162 mx -= hd * my;
163 while (__glibc_unlikely (mx > my))
164 mx -= my;
165 }
166 uint32_t hd = (mx * inv_hy) >> (BIT_WIDTH - exp_diff);
167 mx <<= exp_diff;
168 mx -= hd * my;
169 while (__glibc_unlikely (mx > my))
170 mx -= my;
171
172 return make_float (x: mx, ep: ey, s: sx);
173}
174strong_alias (__fmodf, __ieee754_fmodf)
175#if LIBM_SVID_COMPAT
176versioned_symbol (libm, __fmodf, fmodf, GLIBC_2_38);
177libm_alias_float_other (__fmod, fmod)
178#else
179libm_alias_float (__fmod, fmod)
180#endif
181libm_alias_finite (__ieee754_fmodf, __fmodf)
182

source code of glibc/sysdeps/ieee754/flt-32/e_fmodf.c