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