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 #include "rounding-mode.h"
40
41 #ifndef PICOLIBC_DOUBLE_NOROUND
42 static double
do_round_int(double value,int mode)43 do_round_int(double value, int mode)
44 {
45 switch (mode) {
46 #ifdef FE_TONEAREST
47 case FE_TONEAREST:
48 return round(value);
49 #endif
50 #ifdef FE_UPWARD
51 case FE_UPWARD:
52 return ceil(value);
53 #endif
54 #ifdef FE_DOWNWARD
55 case FE_DOWNWARD:
56 return floor(value);
57 #endif
58 #ifdef FE_TOWARDZERO
59 case FE_TOWARDZERO:
60 return trunc(value);
61 #endif
62 default:
63 return value;
64 }
65 }
66 #endif
67
68 #ifndef PICOLIBC_FLOAT_NOROUND
69 static float
do_roundf_int(float value,int mode)70 do_roundf_int(float value, int mode)
71 {
72 switch (mode) {
73 #ifdef FE_TONEAREST
74 case FE_TONEAREST:
75 return roundf(value);
76 #endif
77 #ifdef FE_UPWARD
78 case FE_UPWARD:
79 return ceilf(value);
80 #endif
81 #ifdef FE_DOWNWARD
82 case FE_DOWNWARD:
83 return floorf(value);
84 #endif
85 #ifdef FE_TOWARDZERO
86 case FE_TOWARDZERO:
87 return truncf(value);
88 #endif
89 default:
90 return value;
91 }
92 }
93 #endif
94
95 double
96 div(double a, double b);
97
98 double
99 mul(double a, double b);
100
101 double
102 sub(double a, double b);
103
104 float
105 div_f(float a, float b);
106
107 float
108 mul_f(float a, float b);
109
110 float
111 sub_f(float a, float b);
112
113 #if defined(FE_UPWARD) && defined(FE_DOWNWARD) && defined(FE_TOWARDZERO)
114
115 #ifndef PICOLIBC_DOUBLE_NOROUND
116
117 static double
div_mul_sub(double a,double b,double c,double d)118 div_mul_sub(double a, double b, double c, double d)
119 {
120 return sub(mul(div(a,b), c), d);
121 }
122
123 #define do_fancy(sign) div_mul_sub(sign 1.0, 3.0, 3.0, sign 1.0)
124
125 #endif
126
127 #ifndef PICOLIBC_FLOAT_NOROUND
128
129 static float
div_f_mul_sub(float a,float b,float c,float d)130 div_f_mul_sub(float a, float b, float c, float d)
131 {
132 return sub_f(mul_f(div_f(a,b), c), d);
133 }
134
135 #define do_fancyf(sign) div_f_mul_sub(sign 1.0f, 3.0f, 3.0f, sign 1.0f)
136
137 #endif
138
139 #endif
140
141 #define n4 nextafter(4.0, 5.0)
142 #define nn4 nextafter(nextafter(4.0, 5.0), 5.0)
143 #define n2 nextafter(2.0, 3.0)
144 #define nn2 nextafter(nextafter(2.0, 3.0), 3.0)
145
146 #define n4f nextafterf(4.0f, 5.0f)
147 #define nn4f nextafterf(nextafterf(4.0f, 5.0f), 5.0f)
148 #define n2f nextafterf(2.0f, 3.0f)
149
150 static int
check(int mode,char * name,double value)151 check(int mode, char *name, double value)
152 {
153 int ret = 0;
154
155 (void) mode;
156 (void) name;
157 (void) value;
158 #ifndef PICOLIBC_DOUBLE_NOROUND
159 double want, got;
160 printf("test double %s for value %g \n", name, value);
161 want = do_round_int(value, mode);
162 if (fesetround(mode) != 0) {
163 printf("ERROR fesetround %s failed\n", name);
164 ret++;
165 }
166 /* Make sure setting an invalid rounding mode fails and doesn't
167 * change the test results
168 */
169 if (fesetround(FE_ALL_EXCEPT+1) == 0) {
170 printf("ERROR fesetround %d succeeded\n", FE_ALL_EXCEPT+1);
171 ret++;
172 }
173 if (fegetround() != mode) {
174 printf("ERROR fegetround() != %s\n", name);
175 ret++;
176 }
177 got = nearbyint(value);
178 if (want != got) {
179 printf("ERROR double %s: value %g want %g got %g\n", name, value, want, got);
180 ret++;
181 }
182
183 want = do_round_int(-value, mode);
184 if (fesetround(mode) != 0) {
185 printf("ERROR fesetround %s failed\n", name);
186 ret++;
187 }
188 got = nearbyint(-value);
189 if (want != got) {
190 printf("ERROR double %s: -value %g want %g got %g\n", name, value, want, got);
191 ret++;
192 }
193 #endif
194 #ifndef PICOLIBC_FLOAT_NOROUND
195 float valuef = value, wantf, gotf;
196
197 printf("test float %s for value %g \n", name, value);
198 wantf = do_roundf_int(valuef, mode);
199 if (fesetround(mode) != 0) {
200 printf("ERROR fesetround %s failed\n", name);
201 ret++;
202 }
203 /* Make sure setting an invalid rounding mode fails and doesn't
204 * change the test results
205 */
206 if (fesetround(FE_ALL_EXCEPT+1) == 0) {
207 printf("ERROR fesetround %d succeeded\n", FE_ALL_EXCEPT+1);
208 ret++;
209 }
210 if (fegetround() != mode) {
211 printf("ERROR fegetround() != %s\n", name);
212 ret++;
213 }
214 gotf = nearbyintf(valuef);
215 if (wantf != gotf) {
216 printf("ERROR float %s: value %g want %g got %g\n", name, (double) valuef, (double) wantf, (double) gotf);
217 ret++;
218 }
219
220 wantf = do_roundf_int(-valuef, mode);
221 if (fesetround(mode) != 0) {
222 printf("ERROR fesetround %s failed\n", name);
223 ret++;
224 }
225 gotf = nearbyintf(-valuef);
226 if (wantf != gotf) {
227 printf("ERROR float %s: -value %g want %g got %g\n", name, (double) valuef, (double) wantf, (double) gotf);
228 ret++;
229 }
230 #endif
231 if (ret)
232 printf("ERROR %s failed\n", name);
233 return ret;
234 }
235
236 static double my_values[] = {
237 1.0,
238 1.0 / 3.0,
239 3.0 / 2.0, /* let either round out or round even work */
240 2.0 / 3.0,
241 };
242
243 #define NUM_VALUE (sizeof(my_values)/sizeof(my_values[0]))
244
main(void)245 int main(void)
246 {
247 unsigned i;
248 int ret = 0;
249
250 for (i = 0; i < NUM_VALUE; i++) {
251 #ifdef FE_TONEAREST
252 ret += check(FE_TONEAREST, "FE_TONEAREST", my_values[i]);
253 #endif
254 #ifdef FE_UPWARD
255 ret += check(FE_UPWARD, "FE_UPWARD", my_values[i]);
256 #endif
257 #ifdef FE_DOWNWARD
258 ret += check(FE_DOWNWARD, "FE_DOWNWARD", my_values[i]);
259 #endif
260 #ifdef FE_TOWARDZERO
261 ret += check(FE_TOWARDZERO, "FE_TOWARDZERO", my_values[i]);
262 #endif
263 }
264 #if defined(FE_UPWARD) && defined(FE_DOWNWARD) && defined(FE_TOWARDZERO)
265
266 #define check_func(big, small, name) do { \
267 printf("testing %s\n", name); \
268 if (big <= small) { \
269 printf("ERROR %s: %g is not > %g\n", name, (double) big, (double) small); \
270 ret++; \
271 } \
272 if (big < 0) { \
273 printf("ERROR %s: %g is not >= 0\n", name, (double) big); \
274 ret++; \
275 } \
276 if (small > 0) { \
277 printf("ERROR %s: %g is not <= 0\n", name, (double) small); \
278 ret++; \
279 } \
280 } while(0)
281
282
283 #ifndef PICOLIBC_DOUBLE_NOROUND
284 double up_plus, toward_plus, down_plus, up_minus, toward_minus, down_minus;
285
286 fesetround(FE_UPWARD);
287 up_plus = do_fancy();
288 up_minus = do_fancy(-);
289
290 fesetround(FE_DOWNWARD);
291 down_plus = do_fancy();
292 down_minus = do_fancy(-);
293
294 fesetround(FE_TOWARDZERO);
295 toward_plus = do_fancy();
296 toward_minus = do_fancy(-);
297
298 check_func(up_plus, down_plus, "up/down");
299 check_func(up_plus, toward_plus, "up/toward");
300 check_func(up_minus, down_minus, "-up/-down");
301 check_func(toward_minus, down_minus, "-toward/-down");
302
303 #define check_sqrt(mode, param, expect) do { \
304 fesetround(mode); \
305 double __p = (param); \
306 double __e = (expect); \
307 printf("testing sqrt %s for value %a\n", #mode, __p); \
308 double __r = sqrt(__p); \
309 if (__r != __e) { \
310 printf("ERROR %s: sqrt(%a) got %a expect %a\n", #mode, __p, __r, __e); \
311 ret++; \
312 } \
313 } while(0)
314
315 check_sqrt(FE_TONEAREST, n4, 2.0);
316 check_sqrt(FE_TONEAREST, nn4, n2);
317
318 check_sqrt(FE_UPWARD, n4, n2);
319 check_sqrt(FE_UPWARD, nn4, n2);
320
321 check_sqrt(FE_DOWNWARD, n4, 2.0);
322 check_sqrt(FE_DOWNWARD, nn4, 2.0);
323
324 check_sqrt(FE_TOWARDZERO, n4, 2.0);
325 check_sqrt(FE_TOWARDZERO, nn4, 2.0);
326 #endif
327 #ifndef PICOLIBC_FLOAT_NOROUND
328 float fup_plus, ftoward_plus, fdown_plus, fup_minus, ftoward_minus, fdown_minus;
329
330 fesetround(FE_UPWARD);
331 fup_plus = do_fancyf();
332 fup_minus = do_fancyf(-);
333
334 fesetround(FE_DOWNWARD);
335 fdown_plus = do_fancyf();
336 fdown_minus = do_fancyf(-);
337
338 fesetround(FE_TOWARDZERO);
339 ftoward_plus = do_fancyf();
340 ftoward_minus = do_fancyf(-);
341
342 check_func(fup_plus, fdown_plus, "fup/fdown");
343 check_func(fup_plus, ftoward_plus, "fup/ftoward");
344 check_func(fup_minus, fdown_minus, "-fup/-fdown");
345 check_func(ftoward_minus, fdown_minus, "-ftoward/-fdown");
346
347 #define check_sqrtf(mode, param, expect) do { \
348 fesetround(mode); \
349 float __p = (param); \
350 float __e = (expect); \
351 printf("testing sqrtf %s for value %a\n", #mode, (double) __p); \
352 float __r = sqrtf(__p); \
353 if (__r != __e) { \
354 printf("ERROR %s: sqrtf(%a) got %a expect %a\n", #mode, (double) __p, (double) __r, (double) __e); \
355 ret++; \
356 } \
357 } while(0)
358
359 check_sqrtf(FE_TONEAREST, n4f, 2.0f);
360 check_sqrtf(FE_TONEAREST, nn4f, n2f);
361
362 check_sqrtf(FE_UPWARD, n4f, n2f);
363 check_sqrtf(FE_UPWARD, nn4f, n2f);
364
365 check_sqrtf(FE_DOWNWARD, n4f, 2.0f);
366 check_sqrtf(FE_DOWNWARD, nn4f, 2.0f);
367
368 check_sqrtf(FE_TOWARDZERO, n4f, 2.0f);
369 check_sqrtf(FE_TOWARDZERO, nn4f, 2.0f);
370
371 #endif
372 #endif
373
374 return ret;
375 }
376