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 <errno.h>
25#include <fenv.h>
26#include <float.h>
27#include <math.h>
28#include <stdbool.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <math-tests.h>
33#include <tininess.h>
34
35#include "tst-strtod.h"
36
37/* Non-standard macros expected to be externally defined:
38
39 L_(str): Pastes the appropriate modifier to a string literal str.
40
41 FNPFX: Expands to the correct prefix for the strtod equivalent
42 of type CHAR. (e.g str or wcs).
43
44 CHAR: Expands to the string type being tested (e.g wchar_t or char).
45
46 STRM: Expands to a string literal suitable for printing CHAR* via
47 printf (e.g "%s" or "%ls"). */
48
49#define _CONCAT(a, b) a ## b
50#define CONCAT(a, b) _CONCAT (a, b)
51
52#define STRTO(x) CONCAT (CONCAT (FNPFX, to), x)
53
54#if LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024
55/* This is a stupid hack for IBM long double. This test ignores
56 inexact values for long double due to the limitations of the
57 format. This ensures rounding tests are ignored. */
58# undef ROUNDING_TESTS_long_double
59# define ROUNDING_TESTS_long_double(x) 0
60#endif
61
62/* Generator to create an FTYPE member variabled named FSUF
63 used to populate struct member variables. */
64#define FTYPE_MEMBER(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \
65 FTYPE FSUF;
66
67/* Likewise, but each member is of type bool. */
68#define BOOL_MEMBER(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \
69 bool FSUF;
70
71#define STRUCT_FOREACH_FLOAT_FTYPE GEN_TEST_STRTOD_FOREACH (FTYPE_MEMBER)
72#define STRUCT_FOREACH_FLOAT_BOOL GEN_TEST_STRTOD_FOREACH (BOOL_MEMBER)
73
74/* Define the long double choose (CHOOSE_ld) macro
75 to select the appropriate generated long double
76 value from the generated test data. */
77#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
78/* This is for the long double == double format. */
79# define CHOOSE_ld(f,d,...) d
80#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16381
81/* This is for the Intel extended float format. */
82# define CHOOSE_ld(f,d,ld64i,...) ld64i
83#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16382
84/* This is for the Motorola extended float format. */
85# define CHOOSE_ld(f,d,ld64i,ld64m,...) ld64m
86#elif LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024
87/* This is for the IBM extended double format. */
88# define CHOOSE_ld(f,d,ld64i,ld64m,ld106,...) ld106
89#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
90/* This is for the IEEE binary128 format. */
91# define CHOOSE_ld(f,d,ld64i,ld64m,ld106,ld113,...) ld113
92#else
93# error "unknown long double format"
94#endif
95
96/* Add type specific choosing macros below. */
97#define CHOOSE_f(f,...) f
98#define CHOOSE_f32(f,...) f
99#define CHOOSE_d(f,d,...) d
100#define CHOOSE_f64(f,d,...) d
101#define CHOOSE_f32x(f,d,...) d
102#define CHOOSE_f128(f,d,ld64i,ld64m,ld106,ld113,...) ld113
103/* long double is special, and handled above. _Float16 would require
104 updates to the generator to generate appropriate expectations, and
105 updates to the test inputs to cover difficult rounding cases for
106 _Float16. */
107
108#if __HAVE_FLOAT64X
109# if FLT64X_MANT_DIG == 113 && FLT64X_MAX_EXP == 16384
110# define CHOOSE_f64x(f,d,ld64i,ld64m,ld106,ld113,...) ld113
111# elif (FLT64X_MANT_DIG == 64 \
112 && FLT64X_MAX_EXP == 16384 \
113 && FLT64X_MIN_EXP == -16381)
114# define CHOOSE_f64x(f,d,ld64i,...) ld64i
115# else
116# error "unknown _Float64x format"
117# endif
118#endif
119
120/* Selector for expected result field of a given type. */
121#define _ENTRY(FSUF, FTYPE, FTOSTR, LSUF, CSUF, ...) \
122 CONCAT (CHOOSE_ ## FSUF (__VA_ARGS__), LSUF),
123#define ENTRY(...) \
124 GEN_TEST_STRTOD_FOREACH (_ENTRY, __VA_ARGS__)
125
126/* Selector for boolean exact tag of expected results and that for
127 overflow. */
128#define _XNTRY(FSUF, FTYPE, FTOSTR, LSUF, CSUF, ...) \
129 CHOOSE_ ## FSUF (__VA_ARGS__),
130#define XNTRY(...) \
131 GEN_TEST_STRTOD_FOREACH (_XNTRY, __VA_ARGS__)
132
133/* This is hacky way around the seemingly unavoidable macro
134 expansion of the INFINITY or HUGE_VAL like macros in the
135 above. It is assumed the compiler will implicitly convert
136 the infinity correctly. */
137#define INF INFINITY + 0.0
138
139/* This macro is used in conjunction with the output from the
140 gen-tst-strtod-round utility to select the appropriately
141 rounded long double value for a given format. */
142#define TEST(s, \
143 fx, fd, fdo, fdu, fn, fno, fnu, \
144 fz, fzo, fzu, fu, fuo, fuu, \
145 dx, dd, ddo, ddu, dn, dno, dnu, \
146 dz, dzo, dzu, du, duo, duu, \
147 ld64ix, ld64id, ld64ido, ld64idu, \
148 ld64in, ld64ino, ld64inu, \
149 ld64iz, ld64izo, ld64izu, \
150 ld64iu, ld64iuo, ld64iuu, \
151 ld64mx, ld64md, ld64mdo, ld64mdu, \
152 ld64mn, ld64mno, ld64mnu, \
153 ld64mz, ld64mzo, ld64mzu, \
154 ld64mu, ld64muo, ld64muu, \
155 ld106x, ld106d, ld106do, ld106du, \
156 ld106n, ld106no, ld106nu, \
157 ld106z, ld106zo, ld106zu, \
158 ld106u, ld106uo, ld106uu, \
159 ld113x, ld113d, ld113do, ld113du, \
160 ld113n, ld113no, ld113nu, \
161 ld113z, ld113zo, ld113zu, \
162 ld113u, ld113uo, ld113uu) \
163 { \
164 L_ (s), \
165 { XNTRY (fx, dx, ld64ix, ld64mx, ld106x, ld113x) }, \
166 { \
167 { ENTRY (fn, dn, ld64in, ld64mn, ld106n, ld113n) }, \
168 { ENTRY (fd, dd, ld64id, ld64md, ld106d, ld113d) }, \
169 { ENTRY (fz, dz, ld64iz, ld64mz, ld106z, ld113z) }, \
170 { ENTRY (fu, du, ld64iu, ld64mu, ld106u, ld113u) } \
171 }, \
172 { \
173 { XNTRY (fno, dno, ld64ino, ld64mno, ld106no, ld113no) }, \
174 { XNTRY (fdo, ddo, ld64ido, ld64mdo, ld106do, ld113do) }, \
175 { XNTRY (fzo, dzo, ld64izo, ld64mzo, ld106zo, ld113zo) }, \
176 { XNTRY (fuo, duo, ld64iuo, ld64muo, ld106uo, ld113uo) } \
177 }, \
178 { \
179 { XNTRY (fnu, dnu, ld64inu, ld64mnu, ld106nu, ld113nu) }, \
180 { XNTRY (fdu, ddu, ld64idu, ld64mdu, ld106du, ld113du) }, \
181 { XNTRY (fzu, dzu, ld64izu, ld64mzu, ld106zu, ld113zu) }, \
182 { XNTRY (fuu, duu, ld64iuu, ld64muu, ld106uu, ld113uu) } \
183 } \
184 }
185
186struct test_exactness
187 {
188 STRUCT_FOREACH_FLOAT_BOOL
189 };
190
191struct test_results
192 {
193 STRUCT_FOREACH_FLOAT_FTYPE
194 };
195
196struct test_overflow
197 {
198 STRUCT_FOREACH_FLOAT_BOOL
199 };
200
201struct test_underflow
202 {
203 STRUCT_FOREACH_FLOAT_BOOL
204 };
205
206struct test {
207 const CHAR *s;
208 struct test_exactness exact;
209 struct test_results r[4];
210 struct test_overflow o[4];
211 struct test_underflow u[4];
212};
213
214/* Include the generated test data. */
215#include "tst-strtod-round-data.h"
216
217#define STRX(x) #x
218#define STR(x) STRX (x)
219#define FNPFXS STR (FNPFX)
220
221#ifndef FE_INEXACT
222# define FE_INEXACT 0
223#endif
224
225#ifndef FE_OVERFLOW
226# define FE_OVERFLOW 0
227#endif
228
229#ifndef FE_UNDERFLOW
230# define FE_UNDERFLOW 0
231#endif
232
233#define GEN_ONE_TEST(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \
234{ \
235 feclearexcept (FE_ALL_EXCEPT); \
236 errno = 12345; \
237 FTYPE f = STRTO (FSUF) (s, NULL); \
238 int new_errno = errno; \
239 if (f != expected->FSUF \
240 || (copysign ## CSUF) (1.0 ## LSUF, f) \
241 != (copysign ## CSUF) (1.0 ## LSUF, expected->FSUF)) \
242 { \
243 char efstr[FSTRLENMAX]; \
244 char fstr[FSTRLENMAX]; \
245 FTOSTR (efstr, FSTRLENMAX, "%a", expected->FSUF); \
246 FTOSTR (fstr, FSTRLENMAX, "%a", f); \
247 printf (FNPFXS "to" #FSUF " (" STRM ") returned %s not " \
248 "%s (%s)\n", s, fstr, efstr, mode_name); \
249 if (ROUNDING_TESTS (FTYPE, rnd_mode) || exact->FSUF) \
250 result = 1; \
251 else \
252 printf ("ignoring this inexact result\n"); \
253 } \
254 else \
255 { \
256 if (FE_INEXACT != 0) \
257 { \
258 bool inexact_raised = fetestexcept (FE_INEXACT) != 0; \
259 if (inexact_raised != !exact->FSUF) \
260 { \
261 printf (FNPFXS "to" #FSUF \
262 " (" STRM ") inexact %d " \
263 "not %d\n", s, inexact_raised, \
264 !exact->FSUF); \
265 if (EXCEPTION_TESTS (FTYPE)) \
266 result = 1; \
267 else \
268 printf ("ignoring this exception error\n"); \
269 } \
270 } \
271 if (FE_OVERFLOW != 0) \
272 { \
273 bool overflow_raised \
274 = fetestexcept (FE_OVERFLOW) != 0; \
275 if (overflow_raised != overflow->FSUF) \
276 { \
277 printf (FNPFXS "to" #FSUF \
278 " (" STRM ") overflow %d " \
279 "not %d\n", s, overflow_raised, \
280 overflow->FSUF); \
281 if (EXCEPTION_TESTS (FTYPE)) \
282 result = 1; \
283 else \
284 printf ("ignoring this exception error\n"); \
285 } \
286 } \
287 if (overflow->FSUF && new_errno != ERANGE) \
288 { \
289 printf (FNPFXS "to" #FSUF \
290 " (" STRM ") left errno == %d," \
291 " not %d (ERANGE)\n", \
292 s, new_errno, ERANGE); \
293 result = 1; \
294 } \
295 if (FE_UNDERFLOW != 0) \
296 { \
297 bool underflow_raised \
298 = fetestexcept (FE_UNDERFLOW) != 0; \
299 if (underflow_raised != underflow->FSUF) \
300 { \
301 printf (FNPFXS "to" #FSUF \
302 " (" STRM ") underflow %d " \
303 "not %d\n", s, underflow_raised, \
304 underflow->FSUF); \
305 if (EXCEPTION_TESTS (FTYPE)) \
306 result = 1; \
307 else \
308 printf ("ignoring this exception error\n"); \
309 } \
310 } \
311 if (underflow->FSUF && new_errno != ERANGE) \
312 { \
313 printf (FNPFXS "to" #FSUF \
314 " (" STRM ") left errno == %d," \
315 " not %d (ERANGE)\n", \
316 s, new_errno, ERANGE); \
317 result = 1; \
318 } \
319 if (!overflow->FSUF \
320 && !underflow->FSUF \
321 && new_errno != 12345) \
322 { \
323 printf (FNPFXS "to" #FSUF \
324 " (" STRM ") set errno == %d," \
325 " should be unchanged\n", \
326 s, new_errno); \
327 result = 1; \
328 } \
329 } \
330}
331
332static int
333test_in_one_mode (const CHAR *s, const struct test_results *expected,
334 const struct test_exactness *exact,
335 const struct test_overflow *overflow,
336 const struct test_underflow *underflow,
337 const char *mode_name, int rnd_mode)
338{
339 int result = 0;
340 GEN_TEST_STRTOD_FOREACH (GEN_ONE_TEST)
341 return result;
342}
343
344static const struct fetestmodes
345 {
346 const char *mode_name;
347 int rnd_mode;
348 int rnd_i; /* Corresponding index into r array of struct test. */
349 } modes[] = {
350 { "default rounding mode", FE_TONEAREST, 0 },
351#ifdef FE_DOWNWARD
352 { "FE_DOWNWARD", FE_DOWNWARD, 1 },
353#endif
354#ifdef FE_TOWARDZERO
355 { "FE_TOWARDZERO", FE_TOWARDZERO, 2 },
356#endif
357#ifdef FE_UPWARD
358 { "FE_UPWARD", FE_UPWARD, 3 },
359#endif
360 {}
361};
362
363static int
364do_test (void)
365{
366 int save_round_mode __attribute__ ((unused)) = fegetround ();
367 int result = 0;
368 for (size_t i = 0; i < sizeof (tests) / sizeof (tests[0]); i++)
369 {
370 result |= test_in_one_mode (tests[i].s, &tests[i].r[modes[0].rnd_i],
371 &tests[i].exact, &tests[i].o[modes[0].rnd_i],
372 &tests[i].u[modes[0].rnd_i],
373 modes[0].mode_name, modes[0].rnd_mode);
374 for (const struct fetestmodes *m = &modes[1]; m->mode_name != NULL; m++)
375 {
376 if (!fesetround (m->rnd_mode))
377 {
378 result |= test_in_one_mode (tests[i].s, &tests[i].r[m->rnd_i],
379 &tests[i].exact,
380 &tests[i].o[m->rnd_i],
381 &tests[i].u[m->rnd_i],
382 m->mode_name,
383 m->rnd_mode);
384 fesetround (save_round_mode);
385 }
386 }
387 }
388 return result;
389}
390
391#define TEST_FUNCTION do_test ()
392#include "../test-skeleton.c"
393

source code of glibc/stdlib/tst-strtod-round-skeleton.c