From patchwork Wed May 25 16:32:09 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Paul E. Murphy" X-Patchwork-Id: 626264 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3rFHr46lrdz9sCj for ; Thu, 26 May 2016 02:32:36 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b=dzfCNa6R; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:subject:date:message-id:in-reply-to :references; q=dns; s=default; b=ZpmVTEoIFt8WICbLHb3oTQhPzepbPJh HiJf510aRqlYQiRsQzMEUIw99IHMS823FmOSfgH/NGa0q099fBS2tFG7TIotBLQX lUz3bJOHx4MDkBVMGXIDZiqzfhRRNvBC3QNkuuf5OGvtoQkV6hl0Jcs5fOxMl9AZ c/bFpB9TilUY= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:subject:date:message-id:in-reply-to :references; s=default; bh=XxPR3eLWuf1+ohguAEdyvwHIbXA=; b=dzfCN a6RES7aspOQ7ZrdgNk6wRi7zTGzVSOV2pDO/bYXdc9baXvBcMpQF0uENZOdzr3j1 Cs/G3rpfrrtNwhFYpDG7zU5yNwS7OSIRgVqlv6UABN70hUCRQrtq8NO5ynYEC2PE nzgyBWfIhEARfBIqU5nEIdKQP/6PB8al1lD2Sc= Received: (qmail 19230 invoked by alias); 25 May 2016 16:32:30 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 19211 invoked by uid 89); 25 May 2016 16:32:29 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.2 required=5.0 tests=AWL, BAYES_00, KAM_LAZY_DOMAIN_SECURITY, RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy=fd X-HELO: e35.co.us.ibm.com X-IBM-Helo: d03dlp01.boulder.ibm.com X-IBM-MailFrom: murphyp@linux.vnet.ibm.com X-IBM-RcptTo: libc-alpha@sourceware.org From: "Paul E. Murphy" To: libc-alpha@sourceware.org Subject: [PATCHv2] Refactor tst-strtod-round.c for type-generic-ness Date: Wed, 25 May 2016 11:32:09 -0500 Message-Id: <1464193929-21482-1-git-send-email-murphyp@linux.vnet.ibm.com> In-Reply-To: References: X-TM-AS-GCONF: 00 X-Content-Scanned: Fidelis XPS MAILER x-cbid: 16052516-0013-0000-0000-000042116232 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused Tested on x86_64 and ppc64le (ldbl-96-intel and ldbl-128ibm). ---8<--- Reduce much of the redundancy in this file, and attempt to coral the type specific stuff to ease adding an new type. NOTE: reviewers, you will need to build gen-tst-strtod-round.c and regenerate tst-strtod-round-data.h. This keeps the patch concise. * stdlib/gen-tst-strtod-round.c: Add backslash to compile command in comment. (printfp): Remove the literal suffix, and define an infinite value as INF to avoid expansion clash with INFINITY. (round_str): Remove the literal suffix. (round_for_all): Likewise, remove the now duplicate ldbl-64 entry, and remove some magic constants. * stdlib/tst-strtod-round.c: (TEST): Redefine to reduce duplication. Remove duplicate dbl-64 and ldbl-64 entries. (ROUNDING_TESTS_long_double): Define as 0 for ibm128. (_CONCAT): New macro. (CONCAT): Likewise. (CHOOSE_ld): Likewise. (CHOOSE_f): Likewise. (CHOOSE_d): Likewise. (FTYPE_MEMBER): Likewise. (BOOL_MEMBER): Likewise. (STRUCT_FOREACH_FLOAT_FTYPE): Likewise. (STRUCT_FOREACH_FLOAT_BOOL): Likewise. (_XNTRY): Likewise. (XNTRY): Likewise. (_ENTRY): Likewise. (ENTRY): Likewise. (test_exactness): Generate members via macro. (test_results): Likewise. (test): Update members. (TEST): Redefine using new macros. (INF): New macro. (fetestmodes): New structure. (do_test): Refactor to be type generic. (test_in_one_mode): Refactor duplicate code into (GEN_ONE_TEST): New macro. * stdlib/tst-strtod-round-data.h: Regenerate. * stdlib/tst-strtod.h (GEN_TEST_STRTOD_FOREACH): Extend to pass additional arbitrary parameters to generators. --- stdlib/gen-tst-strtod-round.c | 48 ++++--- stdlib/tst-strtod-round.c | 304 ++++++++++++++++++++---------------------- stdlib/tst-strtod.h | 8 +- 3 files changed, 173 insertions(+), 187 deletions(-) diff --git a/stdlib/gen-tst-strtod-round.c b/stdlib/gen-tst-strtod-round.c index fa5562e..1c2823f 100644 --- a/stdlib/gen-tst-strtod-round.c +++ b/stdlib/gen-tst-strtod-round.c @@ -19,7 +19,7 @@ /* Compile this program as: - gcc -std=gnu11 -O2 -Wall -Wextra gen-tst-strtod-round.c -lmpfr + gcc -std=gnu11 -O2 -Wall -Wextra gen-tst-strtod-round.c -lmpfr \ -o gen-tst-strtod-round (use of current MPFR version recommended) and run it as: @@ -60,19 +60,18 @@ string_to_fp (mpfr_t f, const char *s, mpfr_rnd_t rnd) #endif } -static void -print_fp (FILE *fout, mpfr_t f, const char *suffix, const char *suffix2) +void +print_fp (FILE *fout, mpfr_t f, const char *suffix) { if (mpfr_inf_p (f)) - mpfr_fprintf (fout, "\t%sINFINITY%s", mpfr_signbit (f) ? "-" : "", - suffix2); + mpfr_fprintf (fout, "\t%sINF%s", mpfr_signbit (f) ? "-" : "", suffix); else - mpfr_fprintf (fout, "\t%Ra%s%s", f, suffix, suffix2); + mpfr_fprintf (fout, "\t%Ra%s", f, suffix); } static void -round_str (FILE *fout, const char *s, const char *suffix, - int prec, int emin, int emax, bool ibm_ld) +round_str (FILE *fout, const char *s, int prec, int emin, int emax, + bool ibm_ld) { mpfr_t f; mpfr_set_default_prec (prec); @@ -94,13 +93,13 @@ round_str (FILE *fout, const char *s, const char *suffix, mpfr_clear (max_value); } mpfr_fprintf (fout, "\t%s,\n", r ? "false" : "true"); - print_fp (fout, f, suffix, ",\n"); + print_fp (fout, f, ",\n"); string_to_fp (f, s, MPFR_RNDN); - print_fp (fout, f, suffix, ",\n"); + print_fp (fout, f, ",\n"); string_to_fp (f, s, MPFR_RNDZ); - print_fp (fout, f, suffix, ",\n"); + print_fp (fout, f, ",\n"); string_to_fp (f, s, MPFR_RNDU); - print_fp (fout, f, suffix, ""); + print_fp (fout, f, ""); mpfr_clear (f); } @@ -108,21 +107,19 @@ static void round_for_all (FILE *fout, const char *s) { static const struct fmt { - const char *suffix; int prec; int emin; int emax; bool ibm_ld; - } formats[7] = { - { "f", 24, -148, 128, false }, - { "", 53, -1073, 1024, false }, - { "L", 53, -1073, 1024, false }, + } formats[] = { + { 24, -148, 128, false }, + { 53, -1073, 1024, false }, /* This is the Intel extended float format. */ - { "L", 64, -16444, 16384, false }, + { 64, -16444, 16384, false }, /* This is the Motorola extended float format. */ - { "L", 64, -16445, 16384, false }, - { "L", 106, -1073, 1024, true }, - { "L", 113, -16493, 16384, false }, + { 64, -16445, 16384, false }, + { 106, -1073, 1024, true }, + { 113, -16493, 16384, false }, }; mpfr_fprintf (fout, " TEST (\""); const char *p; @@ -134,11 +131,12 @@ round_for_all (FILE *fout, const char *s) } mpfr_fprintf (fout, "\",\n"); int i; - for (i = 0; i < 7; i++) + int n_formats = sizeof (formats) / sizeof (formats[0]); + for (i = 0; i < n_formats; i++) { - round_str (fout, s, formats[i].suffix, formats[i].prec, - formats[i].emin, formats[i].emax, formats[i].ibm_ld); - if (i < 6) + round_str (fout, s, formats[i].prec, formats[i].emin, + formats[i].emax, formats[i].ibm_ld); + if (i < n_formats - 1) mpfr_fprintf (fout, ",\n"); } mpfr_fprintf (fout, "),\n"); diff --git a/stdlib/tst-strtod-round.c b/stdlib/tst-strtod-round.c index a2cd2ab..487096c 100644 --- a/stdlib/tst-strtod-round.c +++ b/stdlib/tst-strtod-round.c @@ -29,152 +29,165 @@ #include #include -struct exactness -{ - bool f; - bool d; - bool ld; -}; +#include "tst-strtod.h" -struct test_results { - float f; - double d; - long double ld; -}; +#define _CONCAT(a, b) a ## b +#define CONCAT(a, b) _CONCAT (a, b) -struct test { - const char *s; - struct exactness exact; - struct test_results rd, rn, rz, ru; -}; +#if LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024 +/* This is a stupid hack for IBM long double. This test ignores + inexact values for long double due to the limitations of the + format. This ensures rounding tests are ignored. */ +# undef ROUNDING_TESTS_long_double +# define ROUNDING_TESTS_long_double(x) 0 +#endif +/* Generator to create an FTYPE member variabled named FSUF + used to populate struct member variables. */ +#define FTYPE_MEMBER(FSUF, FTYPE, FTOSTR, FTOSTRM, LSUF, CSUF) \ + FTYPE FSUF; + +/* Likewise, but each member is of type bool. */ +#define BOOL_MEMBER(FSUF, FTYPE, FTOSTR, FTOSTRM, LSUF, CSUF) \ + bool FSUF; + +#define STRUCT_FOREACH_FLOAT_FTYPE GEN_TEST_STRTOD_FOREACH (FTYPE_MEMBER) +#define STRUCT_FOREACH_FLOAT_BOOL GEN_TEST_STRTOD_FOREACH (BOOL_MEMBER) + +/* Define the long double choose (CHOOSE_ld) macro + to select the appropriate generated long double + value from the generated test data. */ #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 -# define TEST(s, fexact, fd, fn, fz, fu, dexact, dd, dn, dz, du, \ - ld53exact, ld53d, ld53n, ld53z, ld53u, \ - ld64iexact, ld64id, ld64in, ld64iz, ld64iu, \ - ld64mexact, ld64md, ld64mn, ld64mz, ld64mu, \ - ld106exact, ld106d, ld106n, ld106z, ld106u, \ - ld113exact, ld113d, ld113n, ld113z, ld113u) \ - { \ - s, \ - { fexact, dexact, ld53exact }, \ - { fd, dd, ld53d }, \ - { fn, dn, ld53n }, \ - { fz, dz, ld53z }, \ - { fu, du, ld53u } \ - } +/* This is for the long double == double format. */ +#define CHOOSE_ld(f,d,...) d #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16381 /* This is for the Intel extended float format. */ -# define TEST(s, fexact, fd, fn, fz, fu, dexact, dd, dn, dz, du, \ - ld53exact, ld53d, ld53n, ld53z, ld53u, \ - ld64iexact, ld64id, ld64in, ld64iz, ld64iu, \ - ld64mexact, ld64md, ld64mn, ld64mz, ld64mu, \ - ld106exact, ld106d, ld106n, ld106z, ld106u, \ - ld113exact, ld113d, ld113n, ld113z, ld113u) \ - { \ - s, \ - { fexact, dexact, ld64iexact }, \ - { fd, dd, ld64id }, \ - { fn, dn, ld64in }, \ - { fz, dz, ld64iz }, \ - { fu, du, ld64iu } \ - } +#define CHOOSE_ld(f,d,ld64i,...) ld64i #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16382 /* This is for the Motorola extended float format. */ -# define TEST(s, fexact, fd, fn, fz, fu, dexact, dd, dn, dz, du, \ - ld53exact, ld53d, ld53n, ld53z, ld53u, \ - ld64iexact, ld64id, ld64in, ld64iz, ld64iu, \ - ld64mexact, ld64md, ld64mn, ld64mz, ld64mu, \ - ld106exact, ld106d, ld106n, ld106z, ld106u, \ - ld113exact, ld113d, ld113n, ld113z, ld113u) \ - { \ - s, \ - { fexact, dexact, ld64mexact }, \ - { fd, dd, ld64md }, \ - { fn, dn, ld64mn }, \ - { fz, dz, ld64mz }, \ - { fu, du, ld64mu } \ - } +#define CHOOSE_ld(f,d,ld64i,ld64m,...) ld64m #elif LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024 -# define TEST(s, fexact, fd, fn, fz, fu, dexact, dd, dn, dz, du, \ - ld53exact, ld53d, ld53n, ld53z, ld53u, \ - ld64iexact, ld64id, ld64in, ld64iz, ld64iu, \ - ld64mexact, ld64md, ld64mn, ld64mz, ld64mu, \ - ld106exact, ld106d, ld106n, ld106z, ld106u, \ - ld113exact, ld113d, ld113n, ld113z, ld113u) \ - { \ - s, \ - { fexact, dexact, ld106exact }, \ - { fd, dd, ld106d }, \ - { fn, dn, ld106n }, \ - { fz, dz, ld106z }, \ - { fu, du, ld106u } \ - } +/* This is for the IBM extended double format. */ +#define CHOOSE_ld(f,d,ld64i,ld64m,ld106,...) ld106 #elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 -# define TEST(s, fexact, fd, fn, fz, fu, dexact, dd, dn, dz, du, \ - ld53exact, ld53d, ld53n, ld53z, ld53u, \ - ld64iexact, ld64id, ld64in, ld64iz, ld64iu, \ - ld64mexact, ld64md, ld64mn, ld64mz, ld64mu, \ - ld106exact, ld106d, ld106n, ld106z, ld106u, \ - ld113exact, ld113d, ld113n, ld113z, ld113u) \ - { \ - s, \ - { fexact, dexact, ld113exact }, \ - { fd, dd, ld113d }, \ - { fn, dn, ld113n }, \ - { fz, dz, ld113z }, \ - { fu, du, ld113u } \ - } +/* This is for the IEEE binary128 format. */ +#define CHOOSE_ld(f,d,ld64i,ld64m,ld106,ld113,...) ld113 #else # error "unknown long double format" #endif +/* Add type specific choosing macros below. */ +#define CHOOSE_f(f,...) f +#define CHOOSE_d(f,d,...) d +/* long double is special, and handled above. */ + +/* Selector for expected result field of a given type. */ +#define _ENTRY(FSUF, FTYPE, FTOSTR, FTOSTRM, LSUF, CSUF, ...) \ + CONCAT (CHOOSE_ ## FSUF (__VA_ARGS__), LSUF), +#define ENTRY(...) \ + GEN_TEST_STRTOD_FOREACH (_ENTRY, __VA_ARGS__) + +/* Selector for boolean exact tag of expected results. */ +#define _XNTRY(FSUF, FTYPE, FTOSTR, FTOSTRM, LSUF, CSUF, ...) \ + CHOOSE_ ## FSUF (__VA_ARGS__), +#define XNTRY(...) \ + GEN_TEST_STRTOD_FOREACH (_XNTRY, __VA_ARGS__) + +/* This is hacky way around the seemingly unavoidable macro + expansion of the INFINITY or HUGE_VAL like macros in the + above. It is assumed the compiler will implicitly convert + the infinity correctly. */ +#define INF INFINITY + 0.0 + +/* This macro is used in conjunction with the output from the + gen-tst-strtod-round utility to select the appropriately + rounded long double value for a given format. */ +#define TEST(s, \ + fx, fd, fn, fz, fu, \ + dx, dd, dn, dz, du, \ + ld64ix, ld64id, ld64in, ld64iz, ld64iu, \ + ld64mx, ld64md, ld64mn, ld64mz, ld64mu, \ + ld106x, ld106d, ld106n, ld106z, ld106u, \ + ld113x, ld113d, ld113n, ld113z, ld113u) \ + { \ + s, \ + { XNTRY (fx, dx, ld64ix, ld64mx, ld106x, ld113x) }, \ + { \ + { ENTRY (fn, dn, ld64in, ld64mn, ld106n, ld113n) }, \ + { ENTRY (fd, dd, ld64id, ld64md, ld106d, ld113d) }, \ + { ENTRY (fz, dz, ld64iz, ld64mz, ld106z, ld113z) }, \ + { ENTRY (fu, du, ld64iu, ld64mu, ld106u, ld113u) } \ + } \ + } + +struct test_exactness + { + STRUCT_FOREACH_FLOAT_BOOL + }; + +struct test_results + { + STRUCT_FOREACH_FLOAT_FTYPE + }; + +struct test { + const char *s; + struct test_exactness exact; + struct test_results r[4]; +}; + /* Include the generated test data. */ #include "tst-strtod-round-data.h" +#define GEN_ONE_TEST(FSUF, FTYPE, FTOSTR, FTOSTRM, LSUF, CSUF) \ +{ \ + FTYPE f = strto ## FSUF (s, NULL); \ + if (f != expected->FSUF \ + || (copysign ## CSUF) (1.0 ## LSUF, f) \ + != (copysign ## CSUF) (1.0 ## LSUF, expected->FSUF)) \ + { \ + char efstr[FSTRLENMAX]; \ + char fstr[FSTRLENMAX]; \ + FTOSTR (efstr, FSTRLENMAX, "%" FTOSTRM "a", expected->FSUF); \ + FTOSTR (fstr, FSTRLENMAX, "%" FTOSTRM "a", f); \ + printf ("strto" #FSUF " (%s) returned %s not %s" \ + " (%s)\n", s, fstr, efstr, mode_name); \ + if (ROUNDING_TESTS (FTYPE, rnd_mode) || exact->FSUF) \ + result = 1; \ + else \ + printf ("ignoring this inexact result\n"); \ + } \ +} + static int test_in_one_mode (const char *s, const struct test_results *expected, - const struct exactness *exact, const char *mode_name, - bool float_round_ok, bool double_round_ok, - bool long_double_round_ok) + const struct test_exactness *exact, const char *mode_name, + int rnd_mode) { int result = 0; - float f = strtof (s, NULL); - double d = strtod (s, NULL); - long double ld = strtold (s, NULL); - if (f != expected->f - || copysignf (1.0f, f) != copysignf (1.0f, expected->f)) - { - printf ("strtof (%s) returned %a not %a (%s)\n", s, f, - expected->f, mode_name); - if (float_round_ok || exact->f) - result = 1; - else - printf ("ignoring this inexact result\n"); - } - if (d != expected->d - || copysign (1.0, d) != copysign (1.0, expected->d)) - { - printf ("strtod (%s) returned %a not %a (%s)\n", s, d, - expected->d, mode_name); - if (double_round_ok || exact->d) - result = 1; - else - printf ("ignoring this inexact result\n"); - } - if (ld != expected->ld - || copysignl (1.0L, ld) != copysignl (1.0L, expected->ld)) - { - printf ("strtold (%s) returned %La not %La (%s)\n", s, ld, - expected->ld, mode_name); - if ((long_double_round_ok && LDBL_MANT_DIG != 106) || exact->ld) - result = 1; - else - printf ("ignoring this inexact result\n"); - } + GEN_TEST_STRTOD_FOREACH (GEN_ONE_TEST) return result; } +static const struct fetestmodes + { + const char *mode_name; + int rnd_mode; + int rnd_i; /* Corresponding index into r array of struct test. */ + } modes[] = { + { "default rounding mode", FE_TONEAREST, 0 }, +#ifdef FE_DOWNWARD + { "FE_DOWNWARD", FE_DOWNWARD, 1 }, +#endif +#ifdef FE_TOWARDZERO + { "FE_TOWARDZERO", FE_TOWARDZERO, 2 }, +#endif +#ifdef FE_UPWARD + { "FE_UPWARD", FE_UPWARD, 3 }, +#endif + {} +}; + static int do_test (void) { @@ -182,44 +195,19 @@ do_test (void) int result = 0; for (size_t i = 0; i < sizeof (tests) / sizeof (tests[0]); i++) { - result |= test_in_one_mode (tests[i].s, &tests[i].rn, &tests[i].exact, - "default rounding mode", - true, true, true); -#ifdef FE_DOWNWARD - if (!fesetround (FE_DOWNWARD)) + result |= test_in_one_mode (tests[i].s, &tests[i].r[modes[0].rnd_i], + &tests[i].exact, modes[0].mode_name, + modes[0].rnd_mode); + for (const struct fetestmodes *m = &modes[1]; m->mode_name != NULL; m++) { - result |= test_in_one_mode (tests[i].s, &tests[i].rd, - &tests[i].exact, "FE_DOWNWARD", - ROUNDING_TESTS (float, FE_DOWNWARD), - ROUNDING_TESTS (double, FE_DOWNWARD), - ROUNDING_TESTS (long double, - FE_DOWNWARD)); - fesetround (save_round_mode); + if (!fesetround (m->rnd_mode)) + { + result |= test_in_one_mode (tests[i].s, &tests[i].r[m->rnd_i], + &tests[i].exact, m->mode_name, + m->rnd_mode); + fesetround (save_round_mode); + } } -#endif -#ifdef FE_TOWARDZERO - if (!fesetround (FE_TOWARDZERO)) - { - result |= test_in_one_mode (tests[i].s, &tests[i].rz, - &tests[i].exact, "FE_TOWARDZERO", - ROUNDING_TESTS (float, FE_TOWARDZERO), - ROUNDING_TESTS (double, FE_TOWARDZERO), - ROUNDING_TESTS (long double, - FE_TOWARDZERO)); - fesetround (save_round_mode); - } -#endif -#ifdef FE_UPWARD - if (!fesetround (FE_UPWARD)) - { - result |= test_in_one_mode (tests[i].s, &tests[i].ru, - &tests[i].exact, "FE_UPWARD", - ROUNDING_TESTS (float, FE_UPWARD), - ROUNDING_TESTS (double, FE_UPWARD), - ROUNDING_TESTS (long double, FE_UPWARD)); - fesetround (save_round_mode); - } -#endif } return result; } diff --git a/stdlib/tst-strtod.h b/stdlib/tst-strtod.h index d53c1de..607cf39 100644 --- a/stdlib/tst-strtod.h +++ b/stdlib/tst-strtod.h @@ -22,10 +22,10 @@ #define FSTRLENMAX 128 /* Splat n variants of the same test for the various strtod functions. */ -#define GEN_TEST_STRTOD_FOREACH(mfunc) \ - mfunc ( f, float, snprintf, "", f, f) \ - mfunc ( d, double, snprintf, "", , ) \ - mfunc ( ld, long double, snprintf, "L", L, l) +#define GEN_TEST_STRTOD_FOREACH(mfunc, ...) \ + mfunc ( f, float, snprintf, "", f, f, ##__VA_ARGS__) \ + mfunc ( d, double, snprintf, "", , , ##__VA_ARGS__) \ + mfunc ( ld, long double, snprintf, "L", L, l, ##__VA_ARGS__) /* The arguments to the generated macros are: FSUF - Function suffix FTYPE - float type