1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright © 2021 Keith Packard
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above
14  *    copyright notice, this list of conditions and the following
15  *    disclaimer in the documentation and/or other materials provided
16  *    with the distribution.
17  *
18  * 3. Neither the name of the copyright holder nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33  * OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <stdio.h>
37 #include <math.h>
38 #include <fenv.h>
39 
40 #ifndef PICOLIBC_DOUBLE_NOROUND
41 static double
do_round_int(double value,int mode)42 do_round_int(double value, int mode)
43 {
44 	switch (mode) {
45 #ifdef FE_TONEAREST
46 	case FE_TONEAREST:
47 		return round(value);
48 #endif
49 #ifdef FE_UPWARD
50 	case FE_UPWARD:
51 		return ceil(value);
52 #endif
53 #ifdef FE_DOWNWARD
54 	case FE_DOWNWARD:
55 		return floor(value);
56 #endif
57 #ifdef FE_TOWARDZERO
58 	case FE_TOWARDZERO:
59 		return trunc(value);
60 #endif
61 	default:
62 		return value;
63 	}
64 }
65 #endif
66 
67 #ifndef PICOLIBC_FLOAT_NOROUND
68 static float
do_roundf_int(float value,int mode)69 do_roundf_int(float value, int mode)
70 {
71 	switch (mode) {
72 #ifdef FE_TONEAREST
73 	case FE_TONEAREST:
74 		return roundf(value);
75 #endif
76 #ifdef FE_UPWARD
77 	case FE_UPWARD:
78 		return ceilf(value);
79 #endif
80 #ifdef FE_DOWNWARD
81 	case FE_DOWNWARD:
82 		return floorf(value);
83 #endif
84 #ifdef FE_TOWARDZERO
85 	case FE_TOWARDZERO:
86 		return truncf(value);
87 #endif
88 	default:
89 		return value;
90 	}
91 }
92 #endif
93 
94 double
95 div(double a, double b);
96 
97 double
98 mul(double a, double b);
99 
100 double
101 sub(double a, double b);
102 
103 float
104 div_f(float a, float b);
105 
106 float
107 mul_f(float a, float b);
108 
109 float
110 sub_f(float a, float b);
111 
112 double
div_mul_sub(double a,double b,double c,double d)113 div_mul_sub(double a, double b, double c, double d)
114 {
115 	return sub(mul(div(a,b), c), d);
116 }
117 
118 #define do_fancy(sign) div_mul_sub(sign 1.0, 3.0, 3.0, sign 1.0)
119 
120 float
div_f_mul_sub(float a,float b,float c,float d)121 div_f_mul_sub(float a, float b, float c, float d)
122 {
123 	return sub_f(mul_f(div_f(a,b), c), d);
124 }
125 
126 #define do_fancyf(sign) div_f_mul_sub(sign 1.0f, 3.0f, 3.0f, sign 1.0f)
127 
128 #define n4      nextafter(4.0, 5.0)
129 #define nn4     nextafter(nextafter(4.0, 5.0), 5.0)
130 #define n2      nextafter(2.0, 3.0)
131 #define nn2     nextafter(nextafter(2.0, 3.0), 3.0)
132 
133 #define n4f     nextafterf(4.0f, 5.0f)
134 #define nn4f    nextafterf(nextafterf(4.0f, 5.0f), 5.0f)
135 #define n2f     nextafterf(2.0f, 3.0f)
136 
137 static int
check(int mode,char * name,double value)138 check(int mode, char *name, double value)
139 {
140 	int ret = 0;
141 
142         (void) mode;
143         (void) name;
144         (void) value;
145 #ifndef PICOLIBC_DOUBLE_NOROUND
146 	double want, got;
147 	printf("test double %s for value %g \n", name, value);
148 	want = do_round_int(value, mode);
149 	if (fesetround(mode) != 0) {
150 		printf("ERROR fesetround %s failed\n", name);
151 		ret++;
152 	}
153 	got = nearbyint(value);
154 	if (want != got) {
155 		printf("ERROR double %s: value %g want %g got %g\n", name, value, want, got);
156 		ret++;
157 	}
158 
159 	want = do_round_int(-value, mode);
160 	if (fesetround(mode) != 0) {
161 		printf("ERROR fesetround %s failed\n", name);
162 		ret++;
163 	}
164 	got = nearbyint(-value);
165 	if (want != got) {
166 		printf("ERROR double %s: -value %g want %g got %g\n", name, value, want, got);
167 		ret++;
168 	}
169 #endif
170 #ifndef PICOLIBC_FLOAT_NOROUND
171 	float valuef = value, wantf, gotf;
172 
173 	printf("test float %s for value %g \n", name, value);
174 	wantf = do_roundf_int(valuef, mode);
175 	if (fesetround(mode) != 0) {
176 		printf("ERROR fesetround %s failed\n", name);
177 		ret++;
178 	}
179 	gotf = nearbyintf(valuef);
180 	if (wantf != gotf) {
181 		printf("ERROR float %s: value %g want %g got %g\n", name, (double) valuef, (double) wantf, (double) gotf);
182 		ret++;
183 	}
184 
185 	wantf = do_roundf_int(-valuef, mode);
186 	if (fesetround(mode) != 0) {
187 		printf("ERROR fesetround %s failed\n", name);
188 		ret++;
189 	}
190 	gotf = nearbyintf(-valuef);
191 	if (wantf != gotf) {
192 		printf("ERROR float %s: -value %g want %g got %g\n", name, (double) valuef, (double) wantf, (double) gotf);
193 		ret++;
194 	}
195 #endif
196 	if (ret)
197 		printf("ERROR %s failed\n", name);
198 	return ret;
199 }
200 
201 static double my_values[] = {
202 	1.0,
203 	1.0 / 3.0,
204 	3.0 / 2.0,	/* let either round out or round even work */
205 	2.0 / 3.0,
206 };
207 
208 #define NUM_VALUE (sizeof(my_values)/sizeof(my_values[0]))
209 
main(void)210 int main(void)
211 {
212 	unsigned i;
213 	int ret = 0;
214 
215 	for (i = 0; i < NUM_VALUE; i++) {
216 #ifdef FE_TONEAREST
217 		ret += check(FE_TONEAREST, "FE_TONEAREST", my_values[i]);
218 #endif
219 #ifdef FE_UPWARD
220 		ret += check(FE_UPWARD, "FE_UPWARD", my_values[i]);
221 #endif
222 #ifdef FE_DOWNWARD
223 		ret += check(FE_DOWNWARD, "FE_DOWNWARD", my_values[i]);
224 #endif
225 #ifdef FE_TOWARDZERO
226 		ret += check(FE_TOWARDZERO, "FE_TOWARDZERO", my_values[i]);
227 #endif
228 	}
229 #if defined(FE_UPWARD) && defined(FE_DOWNWARD) && defined(FE_TOWARDZERO)
230 
231 #define check_func(big, small, name) do {				\
232 		printf("testing %s\n", name); \
233 		if (big <= small) {					\
234 			printf("ERROR %s: %g is not > %g\n", name, (double) big, (double) small); \
235 			ret++;						\
236 		}							\
237 		if (big < 0) {						\
238 			printf("ERROR %s: %g is not >= 0\n", name, (double) big); \
239 			ret++;						\
240 		}							\
241 		if (small > 0) {					\
242 			printf("ERROR %s: %g is not <= 0\n", name, (double) small); \
243 			ret++;						\
244 		}							\
245 	} while(0)
246 
247 
248 #ifndef PICOLIBC_DOUBLE_NOROUND
249 	double up_plus, toward_plus, down_plus, up_minus, toward_minus, down_minus;
250 
251 	fesetround(FE_UPWARD);
252 	up_plus = do_fancy();
253 	up_minus = do_fancy(-);
254 
255 	fesetround(FE_DOWNWARD);
256 	down_plus = do_fancy();
257 	down_minus = do_fancy(-);
258 
259 	fesetround(FE_TOWARDZERO);
260 	toward_plus = do_fancy();
261 	toward_minus = do_fancy(-);
262 
263 	check_func(up_plus, down_plus, "up/down");
264 	check_func(up_plus, toward_plus, "up/toward");
265 	check_func(up_minus, down_minus, "-up/-down");
266 	check_func(toward_minus, down_minus, "-toward/-down");
267 
268 #define check_sqrt(mode, param, expect) do {                            \
269                 fesetround(mode);                                       \
270                 double __p = (param);                                   \
271                 double __e = (expect);                                  \
272                 printf("testing sqrt %s for value %a\n", #mode, __p);   \
273                 double __r = sqrt(__p);                                 \
274                 if (__r != __e) {                                       \
275                         printf("ERROR %s: sqrt(%a) got %a expect %a\n", #mode, __p, __r, __e); \
276                         ret++;                                          \
277                 }                                                       \
278         } while(0)
279 
280         check_sqrt(FE_TONEAREST, n4, 2.0);
281         check_sqrt(FE_TONEAREST, nn4, n2);
282 
283         check_sqrt(FE_UPWARD, n4, n2);
284         check_sqrt(FE_UPWARD, nn4, n2);
285 
286         check_sqrt(FE_DOWNWARD, n4, 2.0);
287         check_sqrt(FE_DOWNWARD, nn4, 2.0);
288 
289         check_sqrt(FE_TOWARDZERO, n4, 2.0);
290         check_sqrt(FE_TOWARDZERO, nn4, 2.0);
291 #endif
292 #ifndef PICOLIBC_FLOAT_NOROUND
293 	float fup_plus, ftoward_plus, fdown_plus, fup_minus, ftoward_minus, fdown_minus;
294 
295 	fesetround(FE_UPWARD);
296 	fup_plus = do_fancyf();
297 	fup_minus = do_fancyf(-);
298 
299 	fesetround(FE_DOWNWARD);
300 	fdown_plus = do_fancyf();
301 	fdown_minus = do_fancyf(-);
302 
303 	fesetround(FE_TOWARDZERO);
304 	ftoward_plus = do_fancyf();
305 	ftoward_minus = do_fancyf(-);
306 
307 	check_func(fup_plus, fdown_plus, "fup/fdown");
308 	check_func(fup_plus, ftoward_plus, "fup/ftoward");
309 	check_func(fup_minus, fdown_minus, "-fup/-fdown");
310 	check_func(ftoward_minus, fdown_minus, "-ftoward/-fdown");
311 
312 #define check_sqrtf(mode, param, expect) do {                           \
313                 fesetround(mode);                                       \
314                 float __p = (param);                                    \
315                 float __e = (expect);                                   \
316                 printf("testing sqrtf %s for value %a\n", #mode, (double) __p); \
317                 float __r = sqrtf(__p);                                 \
318                 if (__r != __e) {                                       \
319                     printf("ERROR %s: sqrtf(%a) got %a expect %a\n", #mode, (double) __p, (double) __r, (double) __e); \
320                         ret++;                                          \
321                 }                                                       \
322         } while(0)
323 
324         check_sqrtf(FE_TONEAREST, n4f, 2.0f);
325         check_sqrtf(FE_TONEAREST, nn4f, n2f);
326 
327         check_sqrtf(FE_UPWARD, n4f, n2f);
328         check_sqrtf(FE_UPWARD, nn4f, n2f);
329 
330         check_sqrtf(FE_DOWNWARD, n4f, 2.0f);
331         check_sqrtf(FE_DOWNWARD, nn4f, 2.0f);
332 
333         check_sqrtf(FE_TOWARDZERO, n4f, 2.0f);
334         check_sqrtf(FE_TOWARDZERO, nn4f, 2.0f);
335 
336 #endif
337 #endif
338 
339 	return ret;
340 }
341