1 | /* Test for correct rounding of results of strtod and related |
2 | functions. |
3 | Copyright (C) 2012-2024 Free Software Foundation, Inc. |
4 | This file is part of the GNU C Library. |
5 | |
6 | The GNU C Library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Lesser General Public |
8 | License as published by the Free Software Foundation; either |
9 | version 2.1 of the License, or (at your option) any later version. |
10 | |
11 | The GNU C Library is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | Lesser General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Lesser General Public |
17 | License along with the GNU C Library; if not, see |
18 | <https://www.gnu.org/licenses/>. */ |
19 | |
20 | /* Defining _LIBC_TEST ensures long double math functions are |
21 | declared in the headers. */ |
22 | #define _LIBC_TEST 1 |
23 | #define __STDC_WANT_IEC_60559_TYPES_EXT__ |
24 | #include <fenv.h> |
25 | #include <float.h> |
26 | #include <math.h> |
27 | #include <stdbool.h> |
28 | #include <stdio.h> |
29 | #include <stdlib.h> |
30 | #include <string.h> |
31 | #include <math-tests.h> |
32 | |
33 | #include "tst-strtod.h" |
34 | |
35 | /* Non-standard macros expected to be externally defined: |
36 | |
37 | L_(str): Pastes the appropriate modifier to a string literal str. |
38 | |
39 | FNPFX: Expands to the correct prefix for the strtod equivalent |
40 | of type CHAR. (e.g str or wcs). |
41 | |
42 | CHAR: Expands to the string type being tested (e.g wchar_t or char). |
43 | |
44 | STRM: Expands to a string literal suitable for printing CHAR* via |
45 | printf (e.g "%s" or "%ls"). */ |
46 | |
47 | #define _CONCAT(a, b) a ## b |
48 | #define CONCAT(a, b) _CONCAT (a, b) |
49 | |
50 | #define STRTO(x) CONCAT (CONCAT (FNPFX, to), x) |
51 | |
52 | #if LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024 |
53 | /* This is a stupid hack for IBM long double. This test ignores |
54 | inexact values for long double due to the limitations of the |
55 | format. This ensures rounding tests are ignored. */ |
56 | # undef ROUNDING_TESTS_long_double |
57 | # define ROUNDING_TESTS_long_double(x) 0 |
58 | #endif |
59 | |
60 | /* Generator to create an FTYPE member variabled named FSUF |
61 | used to populate struct member variables. */ |
62 | #define FTYPE_MEMBER(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \ |
63 | FTYPE FSUF; |
64 | |
65 | /* Likewise, but each member is of type bool. */ |
66 | #define BOOL_MEMBER(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \ |
67 | bool FSUF; |
68 | |
69 | #define STRUCT_FOREACH_FLOAT_FTYPE GEN_TEST_STRTOD_FOREACH (FTYPE_MEMBER) |
70 | #define STRUCT_FOREACH_FLOAT_BOOL GEN_TEST_STRTOD_FOREACH (BOOL_MEMBER) |
71 | |
72 | /* Define the long double choose (CHOOSE_ld) macro |
73 | to select the appropriate generated long double |
74 | value from the generated test data. */ |
75 | #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 |
76 | /* This is for the long double == double format. */ |
77 | # define CHOOSE_ld(f,d,...) d |
78 | #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16381 |
79 | /* This is for the Intel extended float format. */ |
80 | # define CHOOSE_ld(f,d,ld64i,...) ld64i |
81 | #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16382 |
82 | /* This is for the Motorola extended float format. */ |
83 | # define CHOOSE_ld(f,d,ld64i,ld64m,...) ld64m |
84 | #elif LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024 |
85 | /* This is for the IBM extended double format. */ |
86 | # define CHOOSE_ld(f,d,ld64i,ld64m,ld106,...) ld106 |
87 | #elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 |
88 | /* This is for the IEEE binary128 format. */ |
89 | # define CHOOSE_ld(f,d,ld64i,ld64m,ld106,ld113,...) ld113 |
90 | #else |
91 | # error "unknown long double format" |
92 | #endif |
93 | |
94 | /* Add type specific choosing macros below. */ |
95 | #define CHOOSE_f(f,...) f |
96 | #define CHOOSE_f32(f,...) f |
97 | #define CHOOSE_d(f,d,...) d |
98 | #define CHOOSE_f64(f,d,...) d |
99 | #define CHOOSE_f32x(f,d,...) d |
100 | #define CHOOSE_f128(f,d,ld64i,ld64m,ld106,ld113,...) ld113 |
101 | /* long double is special, and handled above. _Float16 would require |
102 | updates to the generator to generate appropriate expectations, and |
103 | updates to the test inputs to cover difficult rounding cases for |
104 | _Float16. */ |
105 | |
106 | #if __HAVE_FLOAT64X |
107 | # if FLT64X_MANT_DIG == 113 && FLT64X_MAX_EXP == 16384 |
108 | # define CHOOSE_f64x(f,d,ld64i,ld64m,ld106,ld113,...) ld113 |
109 | # elif (FLT64X_MANT_DIG == 64 \ |
110 | && FLT64X_MAX_EXP == 16384 \ |
111 | && FLT64X_MIN_EXP == -16381) |
112 | # define CHOOSE_f64x(f,d,ld64i,...) ld64i |
113 | # else |
114 | # error "unknown _Float64x format" |
115 | # endif |
116 | #endif |
117 | |
118 | /* Selector for expected result field of a given type. */ |
119 | #define _ENTRY(FSUF, FTYPE, FTOSTR, LSUF, CSUF, ...) \ |
120 | CONCAT (CHOOSE_ ## FSUF (__VA_ARGS__), LSUF), |
121 | #define ENTRY(...) \ |
122 | GEN_TEST_STRTOD_FOREACH (_ENTRY, __VA_ARGS__) |
123 | |
124 | /* Selector for boolean exact tag of expected results and that for |
125 | overflow. */ |
126 | #define _XNTRY(FSUF, FTYPE, FTOSTR, LSUF, CSUF, ...) \ |
127 | CHOOSE_ ## FSUF (__VA_ARGS__), |
128 | #define XNTRY(...) \ |
129 | GEN_TEST_STRTOD_FOREACH (_XNTRY, __VA_ARGS__) |
130 | |
131 | /* This is hacky way around the seemingly unavoidable macro |
132 | expansion of the INFINITY or HUGE_VAL like macros in the |
133 | above. It is assumed the compiler will implicitly convert |
134 | the infinity correctly. */ |
135 | #define INF INFINITY + 0.0 |
136 | |
137 | /* This macro is used in conjunction with the output from the |
138 | gen-tst-strtod-round utility to select the appropriately |
139 | rounded long double value for a given format. */ |
140 | #define TEST(s, \ |
141 | fx, fd, fdo, fn, fno, fz, fzo, fu, fuo, \ |
142 | dx, dd, ddo, dn, dno, dz, dzo, du, duo, \ |
143 | ld64ix, ld64id, ld64ido, ld64in, ld64ino, \ |
144 | ld64iz, ld64izo, ld64iu, ld64iuo, \ |
145 | ld64mx, ld64md, ld64mdo, ld64mn, ld64mno, \ |
146 | ld64mz, ld64mzo, ld64mu, ld64muo, \ |
147 | ld106x, ld106d, ld106do, ld106n, ld106no, \ |
148 | ld106z, ld106zo, ld106u, ld106uo, \ |
149 | ld113x, ld113d, ld113do, ld113n, ld113no, \ |
150 | ld113z, ld113zo, ld113u, ld113uo) \ |
151 | { \ |
152 | L_ (s), \ |
153 | { XNTRY (fx, dx, ld64ix, ld64mx, ld106x, ld113x) }, \ |
154 | { \ |
155 | { ENTRY (fn, dn, ld64in, ld64mn, ld106n, ld113n) }, \ |
156 | { ENTRY (fd, dd, ld64id, ld64md, ld106d, ld113d) }, \ |
157 | { ENTRY (fz, dz, ld64iz, ld64mz, ld106z, ld113z) }, \ |
158 | { ENTRY (fu, du, ld64iu, ld64mu, ld106u, ld113u) } \ |
159 | }, \ |
160 | { \ |
161 | { XNTRY (fno, dno, ld64ino, ld64mno, ld106no, ld113no) }, \ |
162 | { XNTRY (fdo, ddo, ld64ido, ld64mdo, ld106do, ld113do) }, \ |
163 | { XNTRY (fzo, dzo, ld64izo, ld64mzo, ld106zo, ld113zo) }, \ |
164 | { XNTRY (fuo, duo, ld64iuo, ld64muo, ld106uo, ld113uo) } \ |
165 | } \ |
166 | } |
167 | |
168 | struct test_exactness |
169 | { |
170 | STRUCT_FOREACH_FLOAT_BOOL |
171 | }; |
172 | |
173 | struct test_results |
174 | { |
175 | STRUCT_FOREACH_FLOAT_FTYPE |
176 | }; |
177 | |
178 | struct test_overflow |
179 | { |
180 | STRUCT_FOREACH_FLOAT_BOOL |
181 | }; |
182 | |
183 | struct test { |
184 | const CHAR *s; |
185 | struct test_exactness exact; |
186 | struct test_results r[4]; |
187 | struct test_overflow o[4]; |
188 | }; |
189 | |
190 | /* Include the generated test data. */ |
191 | #include "tst-strtod-round-data.h" |
192 | |
193 | #define STRX(x) #x |
194 | #define STR(x) STRX (x) |
195 | #define FNPFXS STR (FNPFX) |
196 | |
197 | #ifndef FE_INEXACT |
198 | # define FE_INEXACT 0 |
199 | #endif |
200 | |
201 | #ifndef FE_OVERFLOW |
202 | # define FE_OVERFLOW 0 |
203 | #endif |
204 | |
205 | #define GEN_ONE_TEST(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \ |
206 | { \ |
207 | feclearexcept (FE_ALL_EXCEPT); \ |
208 | FTYPE f = STRTO (FSUF) (s, NULL); \ |
209 | if (f != expected->FSUF \ |
210 | || (copysign ## CSUF) (1.0 ## LSUF, f) \ |
211 | != (copysign ## CSUF) (1.0 ## LSUF, expected->FSUF)) \ |
212 | { \ |
213 | char efstr[FSTRLENMAX]; \ |
214 | char fstr[FSTRLENMAX]; \ |
215 | FTOSTR (efstr, FSTRLENMAX, "%a", expected->FSUF); \ |
216 | FTOSTR (fstr, FSTRLENMAX, "%a", f); \ |
217 | printf (FNPFXS "to" #FSUF " (" STRM ") returned %s not " \ |
218 | "%s (%s)\n", s, fstr, efstr, mode_name); \ |
219 | if (ROUNDING_TESTS (FTYPE, rnd_mode) || exact->FSUF) \ |
220 | result = 1; \ |
221 | else \ |
222 | printf ("ignoring this inexact result\n"); \ |
223 | } \ |
224 | else \ |
225 | { \ |
226 | if (FE_INEXACT != 0) \ |
227 | { \ |
228 | bool inexact_raised = fetestexcept (FE_INEXACT) != 0; \ |
229 | if (inexact_raised != !exact->FSUF) \ |
230 | { \ |
231 | printf (FNPFXS "to" #FSUF \ |
232 | " (" STRM ") inexact %d " \ |
233 | "not %d\n", s, inexact_raised, \ |
234 | !exact->FSUF); \ |
235 | if (EXCEPTION_TESTS (FTYPE)) \ |
236 | result = 1; \ |
237 | else \ |
238 | printf ("ignoring this exception error\n"); \ |
239 | } \ |
240 | } \ |
241 | if (FE_OVERFLOW != 0) \ |
242 | { \ |
243 | bool overflow_raised \ |
244 | = fetestexcept (FE_OVERFLOW) != 0; \ |
245 | if (overflow_raised != overflow->FSUF) \ |
246 | { \ |
247 | printf (FNPFXS "to" #FSUF \ |
248 | " (" STRM ") overflow %d " \ |
249 | "not %d\n", s, overflow_raised, \ |
250 | overflow->FSUF); \ |
251 | if (EXCEPTION_TESTS (FTYPE)) \ |
252 | result = 1; \ |
253 | else \ |
254 | printf ("ignoring this exception error\n"); \ |
255 | } \ |
256 | } \ |
257 | } \ |
258 | } |
259 | |
260 | static int |
261 | test_in_one_mode (const CHAR *s, const struct test_results *expected, |
262 | const struct test_exactness *exact, |
263 | const struct test_overflow *overflow, |
264 | const char *mode_name, int rnd_mode) |
265 | { |
266 | int result = 0; |
267 | GEN_TEST_STRTOD_FOREACH (GEN_ONE_TEST) |
268 | return result; |
269 | } |
270 | |
271 | static const struct fetestmodes |
272 | { |
273 | const char *mode_name; |
274 | int rnd_mode; |
275 | int rnd_i; /* Corresponding index into r array of struct test. */ |
276 | } modes[] = { |
277 | { "default rounding mode" , FE_TONEAREST, 0 }, |
278 | #ifdef FE_DOWNWARD |
279 | { "FE_DOWNWARD" , FE_DOWNWARD, 1 }, |
280 | #endif |
281 | #ifdef FE_TOWARDZERO |
282 | { "FE_TOWARDZERO" , FE_TOWARDZERO, 2 }, |
283 | #endif |
284 | #ifdef FE_UPWARD |
285 | { "FE_UPWARD" , FE_UPWARD, 3 }, |
286 | #endif |
287 | {} |
288 | }; |
289 | |
290 | static int |
291 | do_test (void) |
292 | { |
293 | int save_round_mode __attribute__ ((unused)) = fegetround (); |
294 | int result = 0; |
295 | for (size_t i = 0; i < sizeof (tests) / sizeof (tests[0]); i++) |
296 | { |
297 | result |= test_in_one_mode (tests[i].s, &tests[i].r[modes[0].rnd_i], |
298 | &tests[i].exact, &tests[i].o[modes[0].rnd_i], |
299 | modes[0].mode_name, modes[0].rnd_mode); |
300 | for (const struct fetestmodes *m = &modes[1]; m->mode_name != NULL; m++) |
301 | { |
302 | if (!fesetround (m->rnd_mode)) |
303 | { |
304 | result |= test_in_one_mode (tests[i].s, &tests[i].r[m->rnd_i], |
305 | &tests[i].exact, |
306 | &tests[i].o[m->rnd_i], m->mode_name, |
307 | m->rnd_mode); |
308 | fesetround (save_round_mode); |
309 | } |
310 | } |
311 | } |
312 | return result; |
313 | } |
314 | |
315 | #define TEST_FUNCTION do_test () |
316 | #include "../test-skeleton.c" |
317 | |