/* tsprintf.c -- test file for mpfr_sprintf, mpfr_vsprintf, mpfr_snprintf, and mpfr_vsnprintf Copyright 2007-2023 Free Software Foundation, Inc. Contributed by the AriC and Caramba projects, INRIA. This file is part of the GNU MPFR Library. The GNU MPFR Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. The GNU MPFR Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Note: If you use a C99-compatible implementation and GMP (or MPIR) * has been compiled without HAVE_VSNPRINTF defined, then this test * may fail with an error like * repl-vsnprintf.c:389: GNU MP assertion failed: len < total_width * * The reason is that __gmp_replacement_vsnprintf does not support %a/%A, * even though the C library supports it. * * References: * https://sympa.inria.fr/sympa/arc/mpfr/2022-10/msg00001.html * https://sympa.inria.fr/sympa/arc/mpfr/2022-10/msg00027.html * https://gmplib.org/list-archives/gmp-bugs/2022-October/005200.html */ /* Needed due to the tests on HAVE_STDARG and MPFR_USE_MINI_GMP */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #if defined(HAVE_STDARG) && !defined(MPFR_USE_MINI_GMP) #include #include #include #ifdef HAVE_LOCALE_H #include #endif #define MPFR_NEED_INTMAX_H #include "mpfr-test.h" const int prec_max_printf = 5000; /* limit for random precision in random_double() */ #define BUF_SIZE 65536 int randsize; /* 1. compare expected string with the string BUFFER returned by mpfr_sprintf(buffer, fmt, x) 2. then test mpfr_snprintf (buffer, p, fmt, x) with a random p. */ static void check_sprintf (const char *expected, const char *fmt, mpfr_srcptr x) { int n0, n1; char buffer[BUF_SIZE]; /* test mpfr_sprintf */ n0 = mpfr_sprintf (buffer, fmt, x); if (strcmp (buffer, expected) != 0) { printf ("Error in mpfr_sprintf (s, \"%s\", x);\n", fmt); printf ("expected: \"%s\"\ngot: \"%s\"\n", expected, buffer); exit (1); } /* test mpfr_snprintf */ randsize = (int) (randlimb () % (n0 + 3)) - 3; /* between -3 and n0 - 1 */ if (randsize < 0) { n1 = mpfr_snprintf (NULL, 0, fmt, x); } else { buffer[randsize] = 17; n1 = mpfr_snprintf (buffer, randsize, fmt, x); if (buffer[randsize] != 17) { printf ("Buffer overflow in mpfr_snprintf for randsize = %d!\n", randsize); exit (1); } } if (n0 != n1) { printf ("Error in mpfr_snprintf (s, %d, \"%s\", x) return value\n", randsize, fmt); printf ("expected: %d\ngot: %d\nx='", n0, n1); mpfr_printf (fmt, x); printf ("'\n"); exit (1); } if ((randsize > 1 && strncmp (expected, buffer, randsize - 1) != 0) || (randsize == 1 && buffer[0] != '\0')) { char part_expected[BUF_SIZE]; strncpy (part_expected, expected, randsize); part_expected[randsize - 1] = '\0'; printf ("Error in mpfr_vsnprintf (s, %d, \"%s\", ...);\n", randsize, fmt); printf ("expected: \"%s\"\ngot: \"%s\"\n", part_expected, buffer); exit (1); } } /* 1. compare expected string with the string BUFFER returned by mpfr_vsprintf(buffer, fmt, ...) 2. then, test mpfr_vsnprintf. */ static int check_vsprintf (const char *expected, const char *fmt, ...) { int n0, n1; char buffer[BUF_SIZE]; va_list ap0, ap1; va_start (ap0, fmt); n0 = mpfr_vsprintf (buffer, fmt, ap0); va_end (ap0); if (strcmp (buffer, expected) != 0) { printf ("Error in mpfr_vsprintf (s, \"%s\", ...);\n", fmt); printf ("expected: \"%s\"\ngot: \"%s\"\n", expected, buffer); exit (1); } va_start (ap1, fmt); /* test mpfr_snprintf */ randsize = (int) (randlimb () % (n0 + 3)) - 3; /* between -3 and n0 - 1 */ if (randsize < 0) { n1 = mpfr_vsnprintf (NULL, 0, fmt, ap1); } else { buffer[randsize] = 17; n1 = mpfr_vsnprintf (buffer, randsize, fmt, ap1); if (buffer[randsize] != 17) { printf ("Buffer overflow in mpfr_vsnprintf for randsize = %d!\n", randsize); exit (1); } } va_end (ap1); if (n0 != n1) { printf ("Error in mpfr_vsnprintf (s, %d, \"%s\", ...) return value\n", randsize, fmt); printf ("expected: %d\ngot: %d\n", n0, n1); exit (1); } if ((randsize > 1 && strncmp (expected, buffer, randsize - 1) != 0) || (randsize == 1 && buffer[0] != '\0')) { char part_expected[BUF_SIZE]; strncpy (part_expected, expected, randsize); part_expected[randsize - 1] = '\0'; printf ("Error in mpfr_vsnprintf (s, %d, \"%s\", ...);\n", randsize, fmt); printf ("expected: \"%s\"\ngot: \"%s\"\n", part_expected, buffer); exit (1); } return n0; } static void native_types (void) { int c = 'a'; int i = -1; unsigned int ui = 1; double d[] = { -1.25, 7.62939453125e-6 /* 2^(-17) */ }; char s[] = "test"; char buf[255]; int k; sprintf (buf, "%c", c); check_vsprintf (buf, "%c", c); sprintf (buf, "%d", i); check_vsprintf (buf, "%d", i); check_vsprintf ("0", "%d", 0); check_vsprintf ("", "%.d", 0); check_vsprintf ("", "%.0d", 0); sprintf (buf, "%i", i); check_vsprintf (buf, "%i", i); check_vsprintf ("0", "%i", 0); check_vsprintf ("", "%.i", 0); check_vsprintf ("", "%.0i", 0); for (k = 0; k < numberof(d); k++) { sprintf (buf, "%e", d[k]); check_vsprintf (buf, "%e", d[k]); sprintf (buf, "%E", d[k]); check_vsprintf (buf, "%E", d[k]); sprintf (buf, "%f", d[k]); check_vsprintf (buf, "%f", d[k]); sprintf (buf, "%g", d[k]); check_vsprintf (buf, "%g", d[k]); sprintf (buf, "%G", d[k]); check_vsprintf (buf, "%G", d[k]); #if __MPFR_STDC (199901L) gmp_sprintf (buf, "%a", d[k]); check_vsprintf (buf, "%a", d[k]); gmp_sprintf (buf, "%A", d[k]); check_vsprintf (buf, "%A", d[k]); gmp_sprintf (buf, "%la", d[k]); check_vsprintf (buf, "%la", d[k]); gmp_sprintf (buf, "%lA", d[k]); check_vsprintf (buf, "%lA", d[k]); sprintf (buf, "%le", d[k]); check_vsprintf (buf, "%le", d[k]); sprintf (buf, "%lE", d[k]); check_vsprintf (buf, "%lE", d[k]); sprintf (buf, "%lf", d[k]); check_vsprintf (buf, "%lf", d[k]); sprintf (buf, "%lg", d[k]); check_vsprintf (buf, "%lg", d[k]); sprintf (buf, "%lG", d[k]); check_vsprintf (buf, "%lG", d[k]); #endif } sprintf (buf, "%o", i); check_vsprintf (buf, "%o", i); sprintf (buf, "%s", s); check_vsprintf (buf, "%s", s); sprintf (buf, "--%s++", ""); check_vsprintf (buf, "--%s++", ""); sprintf (buf, "%u", ui); check_vsprintf (buf, "%u", ui); sprintf (buf, "%x", ui); check_vsprintf (buf, "%x", ui); } static void special (void) { mpfr_t x; const char *ns[] = { "nan", "NAN", "inf", "INF" }; const char *ps[] = { "", " ", "+", "+ ", " +" }; const char *rs[] = { "", "U", "D", "Y", "Z", "N" }; const char *fs[] = { "efgab", "EFGA" }; int ntests = 0, neg, psflag, r, f; unsigned int i; mpfr_init2 (x, 128); /* initialized to NaN */ for (i = 0; i < numberof (ns); i++) { if (i == 2) MPFR_SET_INF (x); for (neg = 0; neg < 2; neg++) { MPFR_SET_SIGN (x, neg ? MPFR_SIGN_NEG : MPFR_SIGN_POS); for (psflag = 0; psflag < 3; psflag++) for (r = 0; r < numberof (rs); r++) for (f = 0; fs[i & 1][f] != '\0'; f++) { int fmtpsflags = psflag; int zeroflag, width; char ws[4], fmt[20], expected[20], sign, *p; int sp; /* In case of '+' flag, let's randomly test an additional space flag, which should be ignored. */ if (psflag == 2) fmtpsflags += randlimb () % 3; /* '0' flag ignored for NaN and Inf; use it randomly. */ zeroflag = RAND_BOOL (); width = randlimb () % 12; if (width != 0) sprintf (ws, "%d", width); else ws[0] = '\0'; /* The following is a common prefix to better identify error messages produced in this function. */ strcpy (expected, "special "); sprintf (fmt, "%s%%%s%s%sR%s%c", expected, ps[fmtpsflags], zeroflag ? "0" : "", ws, rs[r], fs[i & 1][f]); /* printf ("Format string: \"%s\"\n", fmt); */ p = expected + 8; sign = neg ? '-' : ps[psflag][0]; for (sp = width - (sign != '\0') - 3; sp > 0; sp--) *p++ = ' '; if (sign != '\0') *p++ = sign; strcpy (p, ns[i]); check_sprintf (expected, fmt, x); ntests++; } } } /* 2 base values (NaN and Inf), 2 signs (positive and negative), 3 possible '+'/space flag cases, 6 cases for the rounding mode, 9 format specifiers (efgab / EFGA). */ MPFR_ASSERTN (ntests == 2 * 2 * 3 * 6 * 9); mpfr_clear (x); } static void decimal (void) { mpfr_prec_t p = 128; mpfr_t x, y, z; /* specifier 'P' for precision */ check_vsprintf ("128", "%Pu", p); check_vsprintf ("00128", "%.5Pu", p); check_vsprintf (" 128", "%5Pu", p); check_vsprintf ("000128", "%06Pu", p); check_vsprintf ("128 :", "%-7Pu:", p); check_vsprintf ("000128:", "%-2.6Pd:", p); check_vsprintf (" 000128:", "%8.6Pd:", p); check_vsprintf ("000128 :", "%-8.6Pd:", p); check_vsprintf ("+128:", "%+Pd:", p); check_vsprintf (" 128:", "% Pd:", p); check_vsprintf ("80:", "% Px:", p); check_vsprintf ("0x80:", "% #Px:", p); check_vsprintf ("0x80:", "%0#+ -Px:", p); check_vsprintf ("0200:", "%0#+ -Po:", p); check_vsprintf ("+0000128 :", "%0+ *.*Pd:", -9, 7, p); check_vsprintf ("+12345 :", "%0+ -*.*Pd:", -9, -3, (mpfr_prec_t) 12345); check_vsprintf ("0", "%Pu", (mpfr_prec_t) 0); /* Do not add a test like "%05.1Pd" as MS Windows is buggy: when a precision is given, the '0' flag must be ignored. */ /* specifier 'P' with precision field 0 */ check_vsprintf ("128", "%.Pu", p); check_vsprintf ("128", "%.0Pd", p); check_vsprintf ("", "%.Pu", (mpfr_prec_t) 0); check_vsprintf ("", "%.0Pd", (mpfr_prec_t) 0); mpfr_init (z); mpfr_init2 (x, 128); /* special numbers: tested in special() */ /* positive numbers */ mpfr_set_str (x, "18993474.61279296875", 10, MPFR_RNDN); mpfr_init2 (y, 59); mpfr_set (y, x, MPFR_RNDN); mpfr_set_ui (z, 0, MPFR_RNDD); /* simplest case right justified */ check_sprintf ("1.899347461279296875000000000000000000000e+07", "%30Re", x); check_sprintf (" 1.899347461279296875e+07", "%30Re", y); check_sprintf (" 2e+07", "%30.0Re", x); check_sprintf (" 18993474.612793", "%30Rf", x); check_sprintf (" 18993474.6127930", "%30.7Rf", x); check_sprintf (" 1.89935e+07", "%30Rg", x); check_sprintf (" 2e+07", "%30.0Rg", x); check_sprintf (" 18993474.61279296875", "%30.19Rg", x); check_sprintf (" 0.0000000000000000e+00", "%30Re", z); check_sprintf (" 0.000000", "%30Rf", z); check_sprintf (" 0", "%30Rg", z); check_sprintf (" 0.00000", "%#30Rg", z); check_sprintf (" 0e+00", "%30.0Re", z); check_sprintf (" 0", "%30.0Rf", z); check_sprintf (" 0.0000", "%30.4Rf", z); check_sprintf (" 0", "%30.0Rg", z); check_sprintf (" 0", "%30.4Rg", z); /* sign or space, pad with leading zeros */ check_sprintf (" 1.899347461279296875000000000000000000000E+07", "% 030RE", x); check_sprintf (" 000001.899347461279296875E+07", "% 030RE", y); check_sprintf (" 0000000000000000001.89935E+07", "% 030RG", x); check_sprintf (" 0000000000000000000000002E+07", "% 030.0RE", x); check_sprintf (" 0000000000000000000000000E+00", "% 030.0RE", z); check_sprintf (" 00000000000000000000000000000", "% 030.0RF", z); /* sign + or -, left justified */ check_sprintf ("+1.899347461279296875000000000000000000000e+07", "%+-30Re", x); check_sprintf ("+1.899347461279296875e+07 ", "%+-30Re", y); check_sprintf ("+2e+07 ", "%+-30.0Re", x); check_sprintf ("+0e+00 ", "%+-30.0Re", z); check_sprintf ("+0 ", "%+-30.0Rf", z); /* decimal point, left justified, precision and rounding parameter */ check_vsprintf ("1.9E+07 ", "%#-10.*R*E", 1, MPFR_RNDN, x); check_vsprintf ("2.E+07 ", "%#*.*R*E", -10, 0, MPFR_RNDN, x); check_vsprintf ("2.E+07 ", "%#-10.*R*G", 0, MPFR_RNDN, x); check_vsprintf ("0.E+00 ", "%#-10.*R*E", 0, MPFR_RNDN, z); check_vsprintf ("0. ", "%#-10.*R*F", 0, MPFR_RNDN, z); check_vsprintf ("0. ", "%#-10.*R*G", 0, MPFR_RNDN, z); /* sign or space */ check_sprintf (" 1.899e+07", "% .3RNe", x); check_sprintf (" 2e+07", "% .0RNe", x); /* sign + or -, decimal point, pad with leading zeros */ check_sprintf ("+0001.8E+07", "%0+#11.1RZE", x); check_sprintf ("+00001.E+07", "%0+#11.0RZE", x); check_sprintf ("+0000.0E+00", "%0+#11.1RZE", z); check_sprintf ("+00000000.0", "%0+#11.1RZF", z); /* pad with leading zero */ check_sprintf ("1.899347461279296875000000000000000000000e+07", "%030RDe", x); check_sprintf ("0000001.899347461279296875e+07", "%030RDe", y); check_sprintf ("00000000000000000000000001e+07", "%030.0RDe", x); /* sign or space, decimal point, left justified */ check_sprintf (" 1.8E+07 ", "%- #11.1RDE", x); check_sprintf (" 1.E+07 ", "%- #11.0RDE", x); /* large requested precision */ check_sprintf ("18993474.61279296875", "%.2147483647Rg", x); /* negative numbers */ mpfr_mul_si (x, x, -1, MPFR_RNDD); mpfr_mul_si (z, z, -1, MPFR_RNDD); /* sign + or - */ check_sprintf (" -1.8e+07", "%+10.1RUe", x); check_sprintf (" -1e+07", "%+10.0RUe", x); check_sprintf (" -0e+00", "%+10.0RUe", z); check_sprintf (" -0", "%+10.0RUf", z); /* neighborhood of 1 */ mpfr_set_str (x, "0.99993896484375", 10, MPFR_RNDN); mpfr_set_prec (y, 43); mpfr_set (y, x, MPFR_RNDN); check_sprintf ("9.999389648437500000000000000000000000000E-01", "%-20RE", x); check_sprintf ("9.9993896484375E-01 ", "%-20RE", y); check_sprintf ("1E+00 ", "%-20.RE", x); check_sprintf ("1E+00 ", "%-20.RE", y); check_sprintf ("1E+00 ", "%-20.0RE", x); check_sprintf ("1.0E+00 ", "%-20.1RE", x); check_sprintf ("1.00E+00 ", "%-20.2RE", x); check_sprintf ("9.999E-01 ", "%-20.3RE", x); check_sprintf ("9.9994E-01 ", "%-20.4RE", x); check_sprintf ("0.999939 ", "%-20RF", x); check_sprintf ("1 ", "%-20.RF", x); check_sprintf ("1 ", "%-20.0RF", x); check_sprintf ("1.0 ", "%-20.1RF", x); check_sprintf ("1.00 ", "%-20.2RF", x); check_sprintf ("1.000 ", "%-20.3RF", x); check_sprintf ("0.9999 ", "%-20.4RF", x); check_sprintf ("0.999939 ", "%-#20RF", x); check_sprintf ("1. ", "%-#20.RF", x); check_sprintf ("1. ", "%-#20.0RF", x); check_sprintf ("1.0 ", "%-#20.1RF", x); check_sprintf ("1.00 ", "%-#20.2RF", x); check_sprintf ("1.000 ", "%-#20.3RF", x); check_sprintf ("0.9999 ", "%-#20.4RF", x); check_sprintf ("0.999939 ", "%-20RG", x); check_sprintf ("1 ", "%-20.RG", x); check_sprintf ("1 ", "%-20.0RG", x); check_sprintf ("1 ", "%-20.1RG", x); check_sprintf ("1 ", "%-20.2RG", x); check_sprintf ("1 ", "%-20.3RG", x); check_sprintf ("0.9999 ", "%-20.4RG", x); check_sprintf ("0.999939 ", "%-#20RG", x); check_sprintf ("1. ", "%-#20.RG", x); check_sprintf ("1. ", "%-#20.0RG", x); check_sprintf ("1. ", "%-#20.1RG", x); check_sprintf ("1.0 ", "%-#20.2RG", x); check_sprintf ("1.00 ", "%-#20.3RG", x); check_sprintf ("0.9999 ", "%-#20.4RG", x); /* powers of 10 */ mpfr_set_str (x, "1e17", 10, MPFR_RNDN); check_sprintf ("1.000000000000000000000000000000000000000e+17", "%Re", x); check_sprintf ("1.000e+17", "%.3Re", x); check_sprintf ("100000000000000000", "%.Rf", x); check_sprintf ("100000000000000000", "%.0Rf", x); check_sprintf ("100000000000000000.0", "%.1Rf", x); check_sprintf ("100000000000000000.000000", "%'Rf", x); check_sprintf ("100000000000000000.0", "%'.1Rf", x); mpfr_ui_div (x, 1, x, MPFR_RNDN); /* x=1e-17 */ check_sprintf ("1.000000000000000000000000000000000000000e-17", "%Re", x); check_sprintf ("0.000000", "%Rf", x); check_sprintf ("1e-17", "%Rg", x); check_sprintf ("0.0", "%.1RDf", x); check_sprintf ("0.0", "%.1RZf", x); check_sprintf ("0.1", "%.1RUf", x); check_sprintf ("0.1", "%.1RYf", x); check_sprintf ("0", "%.0RDf", x); check_sprintf ("0", "%.0RZf", x); check_sprintf ("1", "%.0RUf", x); check_sprintf ("1", "%.0RYf", x); /* powers of 10 with 'g' style */ mpfr_set_str (x, "10", 10, MPFR_RNDN); check_sprintf ("10", "%Rg", x); check_sprintf ("1e+01", "%.0Rg", x); check_sprintf ("1e+01", "%.1Rg", x); check_sprintf ("10", "%.2Rg", x); mpfr_ui_div (x, 1, x, MPFR_RNDN); check_sprintf ("0.1", "%Rg", x); check_sprintf ("0.1", "%.0Rg", x); check_sprintf ("0.1", "%.1Rg", x); mpfr_set_str (x, "1000", 10, MPFR_RNDN); check_sprintf ("1000", "%Rg", x); check_sprintf ("1e+03", "%.0Rg", x); check_sprintf ("1e+03", "%.3Rg", x); check_sprintf ("1000", "%.4Rg", x); check_sprintf ("1e+03", "%.3Rg", x); check_sprintf ("1000", "%.4Rg", x); check_sprintf (" 1e+03", "%9.3Rg", x); check_sprintf (" 1000", "%9.4Rg", x); check_sprintf ("00001e+03", "%09.3Rg", x); check_sprintf ("000001000", "%09.4Rg", x); mpfr_ui_div (x, 1, x, MPFR_RNDN); check_sprintf ("0.001", "%Rg", x); check_sprintf ("0.001", "%.0Rg", x); check_sprintf ("0.001", "%.1Rg", x); mpfr_set_str (x, "100000", 10, MPFR_RNDN); check_sprintf ("100000", "%Rg", x); check_sprintf ("1e+05", "%.0Rg", x); check_sprintf ("1e+05", "%.5Rg", x); check_sprintf ("100000", "%.6Rg", x); check_sprintf (" 1e+05", "%17.5Rg", x); check_sprintf (" 100000", "%17.6Rg", x); check_sprintf ("0000000000001e+05", "%017.5Rg", x); check_sprintf ("00000000000100000", "%017.6Rg", x); mpfr_ui_div (x, 1, x, MPFR_RNDN); check_sprintf ("1e-05", "%Rg", x); check_sprintf ("1e-05", "%.0Rg", x); check_sprintf ("1e-05", "%.1Rg", x); /* check rounding mode */ mpfr_set_str (x, "0.0076", 10, MPFR_RNDN); check_sprintf ("0.007", "%.3RDF", x); check_sprintf ("0.007", "%.3RZF", x); check_sprintf ("0.008", "%.3RF", x); check_sprintf ("0.008", "%.3RUF", x); check_sprintf ("0.008", "%.3RYF", x); check_vsprintf ("0.008", "%.3R*F", MPFR_RNDA, x); /* check limit between %f-style and %g-style */ mpfr_set_str (x, "0.0000999", 10, MPFR_RNDN); check_sprintf ("0.0001", "%.0Rg", x); check_sprintf ("9e-05", "%.0RDg", x); check_sprintf ("0.0001", "%.1Rg", x); check_sprintf ("0.0001", "%.2Rg", x); check_sprintf ("9.99e-05", "%.3Rg", x); /* trailing zeros */ mpfr_set_si_2exp (x, -1, -15, MPFR_RNDN); /* x=-2^-15 */ check_sprintf ("-3.0517578125e-05", "%.30Rg", x); check_sprintf ("-3.051757812500000000000000000000e-05", "%.30Re", x); check_sprintf ("-3.05175781250000000000000000000e-05", "%#.30Rg", x); check_sprintf ("-0.000030517578125000000000000000", "%.30Rf", x); /* bug 20081023 */ check_sprintf ("-3.0517578125e-05", "%.30Rg", x); mpfr_set_str (x, "1.9999", 10, MPFR_RNDN); check_sprintf ("1.999900 ", "%-#10.7RG", x); check_sprintf ("1.9999 ", "%-10.7RG", x); mpfr_set_ui (x, 1, MPFR_RNDN); check_sprintf ("1.", "%#.1Rg", x); check_sprintf ("1. ", "%-#5.1Rg", x); check_sprintf (" 1.0", "%#5.2Rg", x); check_sprintf ("1.00000000000000000000000000000", "%#.30Rg", x); check_sprintf ("1", "%.30Rg", x); mpfr_set_ui (x, 0, MPFR_RNDN); check_sprintf ("0.", "%#.1Rg", x); check_sprintf ("0. ", "%-#5.1Rg", x); check_sprintf (" 0.0", "%#5.2Rg", x); check_sprintf ("0.00000000000000000000000000000", "%#.30Rg", x); check_sprintf ("0", "%.30Rg", x); /* following tests with precision 53 bits */ mpfr_set_prec (x, 53); /* Exponent zero has a plus sign */ mpfr_set_str (x, "-9.95645044213728791504536275169812142849e-01", 10, MPFR_RNDN); check_sprintf ("-1.0e+00", "%- #0.1Re", x); /* Decimal point and no figure after it with '#' flag and 'G' style */ mpfr_set_str (x, "-9.90597761233942053494e-01", 10, MPFR_RNDN); check_sprintf ("-1.", "%- #0.1RG", x); /* precision zero */ mpfr_set_d (x, 9.5, MPFR_RNDN); check_sprintf ("9", "%.0RDf", x); check_sprintf ("10", "%.0RUf", x); mpfr_set_d (x, 19.5, MPFR_RNDN); check_sprintf ("19", "%.0RDf", x); check_sprintf ("20", "%.0RUf", x); mpfr_set_d (x, 99.5, MPFR_RNDN); check_sprintf ("99", "%.0RDf", x); check_sprintf ("100", "%.0RUf", x); mpfr_set_d (x, -9.5, MPFR_RNDN); check_sprintf ("-10", "%.0RDf", x); check_sprintf ("-10", "%.0RYf", x); check_sprintf ("-10", "%.0Rf", x); check_sprintf ("-1e+01", "%.0Re", x); check_sprintf ("-1e+01", "%.0Rg", x); mpfr_set_ui_2exp (x, 1, -1, MPFR_RNDN); check_sprintf ("0", "%.0Rf", x); check_sprintf ("5e-01", "%.0Re", x); check_sprintf ("0.5", "%.0Rg", x); mpfr_set_ui_2exp (x, 3, -1, MPFR_RNDN); check_sprintf ("2", "%.0Rf", x); mpfr_set_ui_2exp (x, 5, -1, MPFR_RNDN); check_sprintf ("2", "%.0Rf", x); mpfr_set_ui (x, 0x1f, MPFR_RNDN); check_sprintf ("0x1p+5", "%.0Ra", x); mpfr_set_ui (x, 3, MPFR_RNDN); check_sprintf ("1p+2", "%.0Rb", x); /* round to next ten power with %f but not with %g */ mpfr_set_str (x, "-6.64464380544039223686e-02", 10, MPFR_RNDN); check_sprintf ("-0.1", "%.1Rf", x); check_sprintf ("-0.0", "%.1RZf", x); check_sprintf ("-0.07", "%.1Rg", x); check_sprintf ("-0.06", "%.1RZg", x); /* round to next ten power and do not remove trailing zeros */ mpfr_set_str (x, "9.98429393291486722006e-02", 10, MPFR_RNDN); check_sprintf ("0.1", "%#.1Rg", x); check_sprintf ("0.10", "%#.2Rg", x); check_sprintf ("0.099", "%#.2RZg", x); /* Halfway cases */ mpfr_set_str (x, "1.5", 10, MPFR_RNDN); check_sprintf ("2e+00", "%.0Re", x); mpfr_set_str (x, "2.5", 10, MPFR_RNDN); check_sprintf ("2e+00", "%.0Re", x); mpfr_set_str (x, "9.5", 10, MPFR_RNDN); check_sprintf ("1e+01", "%.0Re", x); mpfr_set_str (x, "1.25", 10, MPFR_RNDN); check_sprintf ("1.2e+00", "%.1Re", x); mpfr_set_str (x, "1.75", 10, MPFR_RNDN); check_sprintf ("1.8e+00", "%.1Re", x); mpfr_set_str (x, "-0.5", 10, MPFR_RNDN); check_sprintf ("-0", "%.0Rf", x); mpfr_set_str (x, "1.25", 10, MPFR_RNDN); check_sprintf ("1.2", "%.1Rf", x); mpfr_set_str (x, "1.75", 10, MPFR_RNDN); check_sprintf ("1.8", "%.1Rf", x); mpfr_set_str (x, "1.5", 10, MPFR_RNDN); check_sprintf ("2", "%.1Rg", x); mpfr_set_str (x, "2.5", 10, MPFR_RNDN); check_sprintf ("2", "%.1Rg", x); mpfr_set_str (x, "9.25", 10, MPFR_RNDN); check_sprintf ("9.2", "%.2Rg", x); mpfr_set_str (x, "9.75", 10, MPFR_RNDN); check_sprintf ("9.8", "%.2Rg", x); /* assertion failure in r6320 */ mpfr_set_str (x, "-9.996", 10, MPFR_RNDN); check_sprintf ("-10.0", "%.1Rf", x); /* regression in MPFR 3.1.0 (bug introduced in r7761, fixed in r7931) */ check_sprintf ("-10", "%.2Rg", x); mpfr_clears (x, y, z, (mpfr_ptr) 0); } static void hexadecimal (void) { mpfr_t x, z; mpfr_inits2 (64, x, z, (mpfr_ptr) 0); /* special numbers: tested in special() */ /* regular numbers */ mpfr_set_str (x, "FEDCBA9.87654321", 16, MPFR_RNDN); mpfr_set_ui (z, 0, MPFR_RNDZ); /* simplest case right justified */ check_sprintf (" 0xf.edcba987654321p+24", "%25Ra", x); check_sprintf (" 0xf.edcba987654321p+24", "%25RUa", x); check_sprintf (" 0xf.edcba987654321p+24", "%25RDa", x); check_sprintf (" 0xf.edcba987654321p+24", "%25RYa", x); check_sprintf (" 0xf.edcba987654321p+24", "%25RZa", x); check_sprintf (" 0xf.edcba987654321p+24", "%25RNa", x); check_sprintf (" 0x1p+28", "%25.0Ra", x); check_sprintf (" 0x0p+0", "%25.0Ra", z); check_sprintf (" 0x0p+0", "%25Ra", z); check_sprintf (" 0x0.p+0", "%#25Ra", z); /* sign or space, pad with leading zeros */ check_sprintf (" 0X00F.EDCBA987654321P+24", "% 025RA", x); check_sprintf (" 0X000000000000000001P+28", "% 025.0RA", x); check_sprintf (" 0X0000000000000000000P+0", "% 025.0RA", z); /* sign + or -, left justified */ check_sprintf ("+0xf.edcba987654321p+24 ", "%+-25Ra", x); check_sprintf ("+0x1p+28 ", "%+-25.0Ra", x); check_sprintf ("+0x0p+0 ", "%+-25.0Ra", z); /* decimal point, left justified, precision and rounding parameter */ check_vsprintf ("0XF.FP+24 ", "%#-10.*R*A", 1, MPFR_RNDN, x); check_vsprintf ("0X1.P+28 ", "%#-10.*R*A", 0, MPFR_RNDN, x); check_vsprintf ("0X0.P+0 ", "%#-10.*R*A", 0, MPFR_RNDN, z); /* sign or space */ check_sprintf (" 0xf.eddp+24", "% .3RNa", x); check_sprintf (" 0x1p+28", "% .0RNa", x); /* sign + or -, decimal point, pad with leading zeros */ check_sprintf ("+0X0F.EP+24", "%0+#11.1RZA", x); check_sprintf ("+0X00F.P+24", "%0+#11.0RZA", x); check_sprintf ("+0X000.0P+0", "%0+#11.1RZA", z); /* pad with leading zero */ check_sprintf ("0x0000f.edcba987654321p+24", "%026RDa", x); check_sprintf ("0x0000000000000000000fp+24", "%026.0RDa", x); /* sign or space, decimal point, left justified */ check_sprintf (" 0XF.EP+24 ", "%- #11.1RDA", x); check_sprintf (" 0XF.P+24 ", "%- #11.0RDA", x); mpfr_mul_si (x, x, -1, MPFR_RNDD); mpfr_mul_si (z, z, -1, MPFR_RNDD); /* sign + or - */ check_sprintf ("-0xf.ep+24", "%+10.1RUa", x); check_sprintf (" -0xfp+24", "%+10.0RUa", x); check_sprintf (" -0x0p+0", "%+10.0RUa", z); /* rounding bit is zero */ mpfr_set_str (x, "0xF.7", 16, MPFR_RNDN); check_sprintf ("0XFP+0", "%.0RNA", x); /* tie case in round to nearest mode */ mpfr_set_str (x, "0x0.8800000000000000p+3", 16, MPFR_RNDN); check_sprintf ("0x9.p-1", "%#.0RNa", x); mpfr_set_str (x, "-0x0.9800000000000000p+3", 16, MPFR_RNDN); check_sprintf ("-0xap-1", "%.0RNa", x); /* trailing zeros in fractional part */ check_sprintf ("-0X4.C0000000000000000000P+0", "%.20RNA", x); /* rounding bit is one and the first non zero bit is far away */ mpfr_set_prec (x, 1024); mpfr_set_ui_2exp (x, 29, -1, MPFR_RNDN); mpfr_nextabove (x); check_sprintf ("0XFP+0", "%.0RNA", x); /* with more than one limb */ mpfr_set_prec (x, 300); mpfr_set_str (x, "0xf.ffffffffffffffffffffffffffffffffffffffffffffffffffff" "fffffffffffffffff", 16, MPFR_RNDN); check_sprintf ("0x1p+4 [300]", "%.0RNa [300]", x); check_sprintf ("0xfp+0 [300]", "%.0RZa [300]", x); check_sprintf ("0x1p+4 [300]", "%.0RYa [300]", x); check_sprintf ("0xfp+0 [300]", "%.0RDa [300]", x); check_sprintf ("0x1p+4 [300]", "%.0RUa [300]", x); check_sprintf ("0x1.0000000000000000000000000000000000000000p+4", "%.40RNa", x); check_sprintf ("0xf.ffffffffffffffffffffffffffffffffffffffffp+0", "%.40RZa", x); check_sprintf ("0x1.0000000000000000000000000000000000000000p+4", "%.40RYa", x); check_sprintf ("0xf.ffffffffffffffffffffffffffffffffffffffffp+0", "%.40RDa", x); check_sprintf ("0x1.0000000000000000000000000000000000000000p+4", "%.40RUa", x); mpfr_set_str (x, "0xf.7fffffffffffffffffffffffffffffffffffffffffffffffffff" "ffffffffffffffffff", 16, MPFR_RNDN); check_sprintf ("0XFP+0", "%.0RNA", x); check_sprintf ("0XFP+0", "%.0RZA", x); check_sprintf ("0X1P+4", "%.0RYA", x); check_sprintf ("0XFP+0", "%.0RDA", x); check_sprintf ("0X1P+4", "%.0RUA", x); check_sprintf ("0XF.8P+0", "%.1RNA", x); check_sprintf ("0XF.7P+0", "%.1RZA", x); check_sprintf ("0XF.8P+0", "%.1RYA", x); check_sprintf ("0XF.7P+0", "%.1RDA", x); check_sprintf ("0XF.8P+0", "%.1RUA", x); /* do not round up to the next power of the base */ mpfr_set_str (x, "0xf.fffffffffffffffffffffffffffffffffffffeffffffffffffff" "ffffffffffffffffff", 16, MPFR_RNDN); check_sprintf ("0xf.ffffffffffffffffffffffffffffffffffffff00p+0", "%.40RNa", x); check_sprintf ("0xf.fffffffffffffffffffffffffffffffffffffeffp+0", "%.40RZa", x); check_sprintf ("0xf.ffffffffffffffffffffffffffffffffffffff00p+0", "%.40RYa", x); check_sprintf ("0xf.fffffffffffffffffffffffffffffffffffffeffp+0", "%.40RDa", x); check_sprintf ("0xf.ffffffffffffffffffffffffffffffffffffff00p+0", "%.40RUa", x); mpfr_clears (x, z, (mpfr_ptr) 0); } static void binary (void) { mpfr_t x; mpfr_t z; mpfr_inits2 (64, x, z, (mpfr_ptr) 0); /* special numbers: tested in special() */ /* regular numbers */ mpfr_set_str (x, "1110010101.1001101", 2, MPFR_RNDN); mpfr_set_ui (z, 0, MPFR_RNDN); /* simplest case: right justified */ check_sprintf (" 1.1100101011001101p+9", "%25Rb", x); check_sprintf (" 0p+0", "%25Rb", z); check_sprintf (" 0.p+0", "%#25Rb", z); /* sign or space, pad with leading zeros */ check_sprintf (" 0001.1100101011001101p+9", "% 025Rb", x); check_sprintf (" 000000000000000000000p+0", "% 025Rb", z); /* sign + or -, left justified */ check_sprintf ("+1.1100101011001101p+9 ", "%+-25Rb", x); check_sprintf ("+0p+0 ", "%+-25Rb", z); /* sign or space */ check_sprintf (" 1.110p+9", "% .3RNb", x); check_sprintf (" 1.1101p+9", "% .4RNb", x); check_sprintf (" 0.0000p+0", "% .4RNb", z); /* sign + or -, decimal point, pad with leading zeros */ check_sprintf ("+00001.1p+9", "%0+#11.1RZb", x); check_sprintf ("+0001.0p+10", "%0+#11.1RNb", x); check_sprintf ("+000000.p+0", "%0+#11.0RNb", z); /* pad with leading zero */ check_sprintf ("00001.1100101011001101p+9", "%025RDb", x); /* sign or space, decimal point (unused), left justified */ check_sprintf (" 1.1p+9 ", "%- #11.1RDb", x); check_sprintf (" 1.p+9 ", "%- #11.0RDb", x); check_sprintf (" 1.p+10 ", "%- #11.0RUb", x); check_sprintf (" 1.p+9 ", "%- #11.0RZb", x); check_sprintf (" 1.p+10 ", "%- #11.0RYb", x); check_sprintf (" 1.p+10 ", "%- #11.0RNb", x); mpfr_mul_si (x, x, -1, MPFR_RNDD); mpfr_mul_si (z, z, -1, MPFR_RNDD); /* sign + or - */ check_sprintf (" -1.1p+9", "%+10.1RUb", x); check_sprintf (" -0.0p+0", "%+10.1RUb", z); /* precision 0 */ check_sprintf ("-1p+10", "%.0RNb", x); check_sprintf ("-1p+10", "%.0RDb", x); check_sprintf ("-1p+9", "%.0RUb", x); check_sprintf ("-1p+9", "%.0RZb", x); check_sprintf ("-1p+10", "%.0RYb", x); /* round to next base power */ check_sprintf ("-1.0p+10", "%.1RNb", x); check_sprintf ("-1.0p+10", "%.1RDb", x); check_sprintf ("-1.0p+10", "%.1RYb", x); /* do not round to next base power */ check_sprintf ("-1.1p+9", "%.1RUb", x); check_sprintf ("-1.1p+9", "%.1RZb", x); /* rounding bit is zero */ check_sprintf ("-1.11p+9", "%.2RNb", x); /* tie case in round to nearest mode */ check_sprintf ("-1.1100101011001101p+9", "%.16RNb", x); /* trailing zeros in fractional part */ check_sprintf ("-1.110010101100110100000000000000p+9", "%.30RNb", x); mpfr_clears (x, z, (mpfr_ptr) 0); } static void mixed (void) { int n1; int n2; int i = 121; #ifdef PRINTF_L long double d = 1. / 31.; #endif mpf_t mpf; mpq_t mpq; mpz_t mpz; mpfr_t x; mpfr_rnd_t rnd; int k; mpf_init (mpf); mpf_set_ui (mpf, 40); mpf_div_ui (mpf, mpf, 31); /* mpf = 40.0 / 31.0 */ mpq_init (mpq); mpq_set_ui (mpq, 123456, 4567890); mpz_init (mpz); mpz_fib_ui (mpz, 64); mpfr_init (x); mpfr_set_str (x, "-12345678.875", 10, MPFR_RNDN); rnd = MPFR_RNDD; check_vsprintf ("121%", "%i%%", i); check_vsprintf ("121% -1.2345678875000000E+07", "%i%% %RNE", i, x); check_vsprintf ("121, -12345679", "%i, %.0Rf", i, x); check_vsprintf ("10610209857723, -1.2345678875000000e+07", "%Zi, %R*e", mpz, rnd, x); check_vsprintf ("-12345678.9, 121", "%.1Rf, %i", x, i); check_vsprintf ("-12345678, 1e240/45b352", "%.0R*f, %Qx", MPFR_RNDZ, x, mpq); /* TODO: Systematically test with and without %n in check_vsprintf? */ /* Do the test several times due to random parameters in check_vsprintf and the use of %n. In r11501, n2 is incorrect (seems random) when randsize <= 0, i.e. when the size argument of mpfr_vsnprintf is 0. */ for (k = 0; k < 30; k++) { n2 = -17; /* If this value is obtained for n2 after the check_vsprintf call below, this probably means that n2 has not been written as expected. */ n1 = check_vsprintf ("121, -12345678.875000000000, 1.290323", "%i, %.*Rf, %Ff%n", i, 12, x, mpf, &n2); if (n1 != n2) { printf ("error in number of characters written by mpfr_vsprintf" " for k = %d, randsize = %d\n", k, randsize); printf ("expected: %d\n", n2); printf (" got: %d\n", n1); exit (1); } } #ifdef PRINTF_L /* under MinGW, -D__USE_MINGW_ANSI_STDIO is required to support %Lf see https://gcc.gnu.org/legacy-ml/gcc/2013-03/msg00103.html */ check_vsprintf ("00000010610209857723, -1.2345678875000000e+07, 0.032258", "%.*Zi, %R*e, %Lf", 20, mpz, rnd, x, d); #endif /* check invalid spec.spec */ check_vsprintf ("%,", "%,"); check_vsprintf ("%3*Rg", "%3*Rg"); /* check empty format */ check_vsprintf ("%", "%"); mpf_clear (mpf); mpq_clear (mpq); mpz_clear (mpz); mpfr_clear (x); } #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) && MPFR_LCONV_DPTS /* Check with locale "da_DK.utf8" or "da_DK". On most platforms, decimal point is ',' and thousands separator is '.'; if this is not the case or if the locale does not exist, the test is not performed (and if the MPFR_CHECK_LOCALES environment variable is set, the program fails). */ static void locale_da_DK (void) { mpfr_prec_t p = 128; mpfr_t x, y; if ((setlocale (LC_ALL, "da_DK.utf8") == 0 && setlocale (LC_ALL, "da_DK") == 0) || localeconv()->decimal_point[0] != ',' || localeconv()->thousands_sep[0] != '.') { setlocale (LC_ALL, "C"); if (getenv ("MPFR_CHECK_LOCALES") == NULL) return; fprintf (stderr, "Cannot test the da_DK locale (not found or inconsistent).\n"); exit (1); } mpfr_init2 (x, p); /* positive numbers */ mpfr_set_str (x, "18993474.61279296875", 10, MPFR_RNDN); mpfr_init2 (y, 59); mpfr_set (y, x, MPFR_RNDN); /* simplest case right justified with thousands separator */ check_sprintf ("1,899347461279296875000000000000000000000e+07", "%'30Re", x); check_sprintf (" 1,899347461279296875e+07", "%'30Re", y); check_sprintf (" 1,89935e+07", "%'30Rg", x); check_sprintf (" 18.993.474,61279296875", "%'30.19Rg", x); check_sprintf (" 18.993.474,612793", "%'30Rf", x); /* sign or space, pad, thousands separator with leading zeros */ check_sprintf (" 1,899347461279296875000000000000000000000E+07", "%' 030RE", x); check_sprintf (" 000001,899347461279296875E+07", "%' 030RE", y); check_sprintf (" 0000000000000000001,89935E+07", "%' 030RG", x); check_sprintf (" 000000018.993.474,61279296875", "%' 030.19RG", x); check_sprintf (" 00000000000018.993.474,612793", "%' 030RF", x); #define T1 "000" #define T2 ".000" #define S1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 T1 #define S2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 T2 "," mpfr_set_ui (x, 48, MPFR_RNDN); mpfr_exp10 (x, x, MPFR_RNDN); check_sprintf ("1" S1, "%.0Rf", x); check_sprintf ("1" S2, "%'#.0Rf", x); check_sprintf ("1" S2 "0000", "%'.4Rf", x); mpfr_mul_ui (x, x, 10, MPFR_RNDN); check_sprintf ("10" S1, "%.0Rf", x); check_sprintf ("10" S2, "%'#.0Rf", x); check_sprintf ("10" S2 "0000", "%'.4Rf", x); mpfr_mul_ui (x, x, 10, MPFR_RNDN); check_sprintf ("100" S1, "%.0Rf", x); check_sprintf ("100" S2, "%'#.0Rf", x); check_sprintf ("100" S2 "0000", "%'.4Rf", x); mpfr_clear (x); mpfr_clear (y); setlocale (LC_ALL, "C"); } #endif /* ... && MPFR_LCONV_DPTS */ /* check concordance between mpfr_asprintf result with a regular mpfr float and with a regular double float */ static void random_double (void) { mpfr_t x; /* random regular mpfr float */ double y; /* regular double float (equal to x) */ char flag[] = { '-', '+', ' ', '#', '0', /* no ambiguity: first zeros are flag zero */ '\'' /* SUS extension */ }; /* no 'a': mpfr and glibc do not have the same semantic */ char specifier[] = { 'e', 'f', 'g', 'E', 'f', /* SUSv2 doesn't accept %F, but %F and %f are the same for regular numbers */ 'G', }; int spec; /* random index in specifier[] */ int prec; /* random value for precision field */ /* in the format string for mpfr_t variable, the maximum length is reached by something like "%-+ #0'.*Rf", that is 12 characters. */ #define FMT_MPFR_SIZE 12 char fmt_mpfr[FMT_MPFR_SIZE]; char *ptr_mpfr; /* in the format string for double variable, the maximum length is reached by something like "%-+ #0'.*f", that is 11 characters. */ #define FMT_SIZE 11 char fmt[FMT_SIZE]; char *ptr; int xi; char *xs; int yi; char *ys; int i, j, jmax; mpfr_init2 (x, MPFR_LDBL_MANT_DIG); for (i = 0; i < 1000; ++i) { /* 1. random double */ do { y = DBL_RAND (); } while (ABS(y) < DBL_MIN); if (RAND_BOOL ()) y = -y; mpfr_set_d (x, y, MPFR_RNDN); if (y != mpfr_get_d (x, MPFR_RNDN)) /* conversion error: skip this one */ continue; /* 2. build random format strings fmt_mpfr and fmt */ ptr_mpfr = fmt_mpfr; ptr = fmt; *ptr_mpfr++ = *ptr++ = '%'; /* random specifier 'e', 'f', 'g', 'E', 'F', or 'G' */ spec = (int) (randlimb() % 6); /* random flags, but no ' flag with %e or with non-glibc */ #if __MPFR_GLIBC(1,0) jmax = (spec == 0 || spec == 3) ? 5 : 6; #else jmax = 5; #endif for (j = 0; j < jmax; j++) { if (randlimb() % 3 == 0) *ptr_mpfr++ = *ptr++ = flag[j]; } *ptr_mpfr++ = *ptr++ = '.'; *ptr_mpfr++ = *ptr++ = '*'; *ptr_mpfr++ = 'R'; *ptr_mpfr++ = *ptr++ = specifier[spec]; *ptr_mpfr = *ptr = '\0'; MPFR_ASSERTN (ptr - fmt < FMT_SIZE); MPFR_ASSERTN (ptr_mpfr - fmt_mpfr < FMT_MPFR_SIZE); /* advantage small precision */ prec = RAND_BOOL () ? 10 : prec_max_printf; prec = (int) (randlimb () % prec); /* 3. calls and checks */ /* the double float case is handled by the libc asprintf through gmp_asprintf */ xi = mpfr_asprintf (&xs, fmt_mpfr, prec, x); yi = mpfr_asprintf (&ys, fmt, prec, y); /* test if XS and YS differ, beware that ISO C99 doesn't specify the sign of a zero exponent (the C99 rationale says: "The sign of a zero exponent in %e format is unspecified. The committee knows of different implementations and choose not to require implementations to document their behavior in this case (by making this be implementation defined behaviour). Most implementations use a "+" sign, e.g., 1.2e+00; but there is at least one implementation that uses the sign of the unlimited precision result, e.g., the 0.987 would be 9.87e-01, so could end up as 1e-00 after rounding to one digit of precision."), while mpfr always uses '+' */ if (xi != yi || ((strcmp (xs, ys) != 0) && (spec == 1 || spec == 4 || ((strstr (xs, "e+00") == NULL || strstr (ys, "e-00") == NULL) && (strstr (xs, "E+00") == NULL || strstr (ys, "E-00") == NULL))))) { mpfr_printf ("Error in mpfr_asprintf(\"%s\", %d, %Re)\n", fmt_mpfr, prec, x); printf ("expected: %s\n", ys); printf (" got: %s\n", xs); printf ("xi=%d yi=%d spec=%d\n", xi, yi, spec); exit (1); } mpfr_free_str (xs); mpfr_free_str (ys); } mpfr_clear (x); } static void bug20080610 (void) { /* bug on icc found on June 10, 2008 */ /* this is not a bug but a different implementation choice: ISO C99 doesn't specify the sign of a zero exponent (see note in random_double above). */ mpfr_t x; double y; int xi; char *xs; int yi; char *ys; mpfr_init2 (x, MPFR_LDBL_MANT_DIG); y = -9.95645044213728791504536275169812142849e-01; mpfr_set_d (x, y, MPFR_RNDN); xi = mpfr_asprintf (&xs, "%- #0.*Re", 1, x); yi = mpfr_asprintf (&ys, "%- #0.*e", 1, y); if (xi != yi || strcmp (xs, ys) != 0) { printf ("Error in bug20080610\n"); printf ("expected: %s\n", ys); printf (" got: %s\n", xs); printf ("xi=%d yi=%d\n", xi, yi); exit (1); } mpfr_free_str (xs); mpfr_free_str (ys); mpfr_clear (x); } static void bug20081214 (void) { /* problem with glibc 2.3.6, December 14, 2008: the system asprintf outputs "-1.0" instead of "-1.". */ mpfr_t x; double y; int xi; char *xs; int yi; char *ys; mpfr_init2 (x, MPFR_LDBL_MANT_DIG); y = -9.90597761233942053494e-01; mpfr_set_d (x, y, MPFR_RNDN); xi = mpfr_asprintf (&xs, "%- #0.*RG", 1, x); yi = mpfr_asprintf (&ys, "%- #0.*G", 1, y); if (xi != yi || strcmp (xs, ys) != 0) { mpfr_printf ("Error in bug20081214\n" "mpfr_asprintf(\"%- #0.*Re\", 1, %Re)\n", x); printf ("expected: %s\n", ys); printf (" got: %s\n", xs); printf ("xi=%d yi=%d\n", xi, yi); exit (1); } mpfr_free_str (xs); mpfr_free_str (ys); mpfr_clear (x); } static void bug20111102 (void) { mpfr_t t; char s[100]; mpfr_init2 (t, 84); mpfr_set_str (t, "999.99999999999999999999", 10, MPFR_RNDN); mpfr_sprintf (s, "%.20RNg", t); if (strcmp (s, "1000") != 0) { printf ("Error in bug20111102, expected 1000, got %s\n", s); exit (1); } mpfr_clear (t); } /* In particular, the following test makes sure that the rounding * for %Ra and %Rb is not done on the MPFR number itself (as it * would overflow). Note: it has been reported on comp.std.c that * some C libraries behave differently on %a, but this is a bug. */ static void check_emax_aux (mpfr_exp_t e) { mpfr_t x; char *s1, s2[256]; int i; mpfr_exp_t emax; MPFR_ASSERTN (e <= LONG_MAX); emax = mpfr_get_emax (); set_emax (e); mpfr_init2 (x, 16); mpfr_set_inf (x, 1); mpfr_nextbelow (x); i = mpfr_asprintf (&s1, "%Ra %.2Ra", x, x); MPFR_ASSERTN (i > 0); mpfr_snprintf (s2, 256, "0x7.fff8p+%ld 0x8.00p+%ld", e-3, e-3); if (strcmp (s1, s2) != 0) { printf ("Error in check_emax_aux for emax = "); if (e > LONG_MAX) printf ("(>LONG_MAX)\n"); else printf ("%ld\n", (long) e); printf ("Expected '%s'\n", s2); printf ("Got '%s'\n", s1); exit (1); } mpfr_free_str (s1); i = mpfr_asprintf (&s1, "%Rb %.2Rb", x, x); MPFR_ASSERTN (i > 0); mpfr_snprintf (s2, 256, "1.111111111111111p+%ld 1.00p+%ld", e-1, e); if (strcmp (s1, s2) != 0) { printf ("Error in check_emax_aux for emax = "); if (e > LONG_MAX) printf ("(>LONG_MAX)\n"); else printf ("%ld\n", (long) e); printf ("Expected %s\n", s2); printf ("Got %s\n", s1); exit (1); } mpfr_free_str (s1); mpfr_clear (x); set_emax (emax); } static void check_emax (void) { check_emax_aux (15); check_emax_aux (MPFR_EMAX_MAX); } static void check_emin_aux (mpfr_exp_t e) { mpfr_t x; char *s1, s2[256]; int i; mpfr_exp_t emin; mpz_t ee; MPFR_ASSERTN (e >= LONG_MIN); emin = mpfr_get_emin (); set_emin (e); mpfr_init2 (x, 16); mpz_init (ee); mpfr_setmin (x, e); mpz_set_si (ee, e); mpz_sub_ui (ee, ee, 1); i = mpfr_asprintf (&s1, "%Ra", x); MPFR_ASSERTN (i > 0); gmp_snprintf (s2, 256, "0x1p%Zd", ee); if (strcmp (s1, s2) != 0) { printf ("Error in check_emin_aux for emin = %ld\n", (long) e); printf ("Expected %s\n", s2); printf ("Got %s\n", s1); exit (1); } mpfr_free_str (s1); i = mpfr_asprintf (&s1, "%Rb", x); MPFR_ASSERTN (i > 0); gmp_snprintf (s2, 256, "1p%Zd", ee); if (strcmp (s1, s2) != 0) { printf ("Error in check_emin_aux for emin = %ld\n", (long) e); printf ("Expected %s\n", s2); printf ("Got %s\n", s1); exit (1); } mpfr_free_str (s1); mpfr_clear (x); mpz_clear (ee); set_emin (emin); } static void check_emin (void) { check_emin_aux (-15); check_emin_aux (mpfr_get_emin ()); check_emin_aux (MPFR_EMIN_MIN); } static void test20161214 (void) { mpfr_t x; char buf[32]; const char s[] = "0x0.fffffffffffff8p+1024"; int r; mpfr_init2 (x, 64); mpfr_set_str (x, s, 16, MPFR_RNDN); r = mpfr_snprintf (buf, 32, "%.*RDf", -2, x); MPFR_ASSERTN(r == 316); r = mpfr_snprintf (buf, 32, "%.*RDf", INT_MIN + 1, x); MPFR_ASSERTN(r == 316); r = mpfr_snprintf (buf, 32, "%.*RDf", INT_MIN, x); MPFR_ASSERTN(r == 316); mpfr_clear (x); } /* http://gforge.inria.fr/tracker/index.php?func=detail&aid=21056 */ static void bug21056 (void) { mpfr_t x; const char s[] = "0x0.fffffffffffff8p+1024"; int ndigits, r; mpfr_init2 (x, 64); mpfr_set_str (x, s, 16, MPFR_RNDN); ndigits = 1000; r = mpfr_snprintf (0, 0, "%.*RDf", ndigits, x); /* the return value should be ndigits + 310 */ MPFR_ASSERTN(r == ndigits + 310); ndigits = INT_MAX - 310; r = mpfr_snprintf (0, 0, "%.*RDf", ndigits, x); MPFR_ASSERTN(r == INT_MAX); ndigits = INT_MAX - 10; r = mpfr_snprintf (0, 0, "%.*RDa", ndigits, x); MPFR_ASSERTN(r == INT_MAX); ndigits = INT_MAX - 7; r = mpfr_snprintf (0, 0, "%.*RDe", ndigits, x); MPFR_ASSERTN(r == INT_MAX); ndigits = 1000; r = mpfr_snprintf (0, 0, "%.*RDg", ndigits, x); /* since trailing zeros are removed with %g, we get less digits */ MPFR_ASSERTN(r == 309); ndigits = INT_MAX; r = mpfr_snprintf (0, 0, "%.*RDg", ndigits, x); /* since trailing zeros are removed with %g, we get less digits */ MPFR_ASSERTN(r == 309); ndigits = INT_MAX - 1; r = mpfr_snprintf (0, 0, "%#.*RDg", ndigits, x); MPFR_ASSERTN(r == ndigits + 1); mpfr_clear (x); } /* Fails for i = 5, i.e. t[i] = (size_t) UINT_MAX + 1, with r11427 on 64-bit machines (4-byte int, 8-byte size_t). On such machines, t[5] converted to int typically gives 0. Note: the assumed behavior corresponds to the snprintf behavior in ISO C, but this conflicts with POSIX: https://sourceware.org/bugzilla/show_bug.cgi?id=14771#c2 https://austingroupbugs.net/view.php?id=761 https://austingroupbugs.net/view.php?id=1219 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87096 The issue was due to a 64-bit size_t converted to a 32-bit int. Fixed in r11429 (6b8cf3e2bdc285027627281cac230ed932c1b73f) on 2017-04-07. */ static void snprintf_size (void) { mpfr_t x; char buf[12]; const char s[] = "17.00000000"; size_t t[] = { 11, 12, 64, INT_MAX, (size_t) INT_MAX + 1, (size_t) UINT_MAX + 1, (size_t) UINT_MAX + 2, (size_t) -1 }; int i, r; mpfr_init2 (x, 64); mpfr_set_ui (x, 17, MPFR_RNDN); for (i = 0; i < sizeof (t) / sizeof (*t); i++) { memset (buf, 0, sizeof (buf)); /* r = snprintf (buf, t[i], "%.8f", 17.0); */ r = mpfr_snprintf (buf, t[i], "%.8Rf", x); if (r != 11 || (t[i] > 11 && strcmp (buf, s) != 0)) { printf ("Error in snprintf_size for i = %d:\n", i); printf ("expected r = 11, \"%s\"\n", s); printf ("got r = %d, \"%s\"\n", r, buf); exit (1); } } mpfr_clear (x); } /* With r11516, n2 gets a random value for i = 0 only! valgrind detects a problem for "nchar = buf.curr - buf.start;" in the spec.spec == 'n' case. Indeed, there is no buffer when size is 0. */ static void percent_n (void) { int err = 0, i, j; for (i = 0; i < 24; i++) for (j = 0; j < 3; j++) { volatile int n1, n2; char buffer[64]; memset (buffer, 0, 64); n2 = -17; n1 = mpfr_snprintf (buffer, i % 8, "%d%n", 123, &n2); if (n1 != 3 || n2 != 3) { printf ("Error 1 in percent_n: i = %d, n1 = %d, n2 = %d\n", i, n1, n2); err = 1; } } if (err) exit (1); } struct clo { const char *fmt; int width, r, e; }; static void check_length_overflow (void) { mpfr_t x; int i, r, e; struct clo t[] = { { "%Rg", 0, 1, 0 }, { "%*Rg", 1, 1, 0 }, { "%*Rg", -1, 1, 0 }, { "%5Rg", 0, 5, 0 }, { "%*Rg", 5, 5, 0 }, { "%*Rg", -5, 5, 0 }, #if INT_MAX == 2147483647 { "%2147483647Rg", 0, 2147483647, 0 }, { "%2147483647Rg ", 0, -1, 1 }, { "%2147483648Rg", 0, -1, 1 }, { "%18446744073709551616Rg", 0, -1, 1 }, { "%*Rg", 2147483647, 2147483647, 0 }, { "%*Rg", -2147483647, 2147483647, 0 }, # if INT_MIN < -INT_MAX { "%*Rg", INT_MIN, -1, 1 }, # endif #endif }; mpfr_init2 (x, MPFR_PREC_MIN); mpfr_set_ui (x, 0, MPFR_RNDN); for (i = 0; i < numberof (t); i++) { errno = 0; r = t[i].width == 0 ? mpfr_snprintf (NULL, 0, t[i].fmt, x) : mpfr_snprintf (NULL, 0, t[i].fmt, t[i].width, x); e = errno; if ((t[i].r < 0 ? r >= 0 : r != t[i].r) #ifdef EOVERFLOW || (t[i].e && e != EOVERFLOW) #endif ) { printf ("Error in check_length_overflow for i=%d (%s %d)\n", i, t[i].fmt, t[i].width); printf ("Expected r=%d, got r=%d\n", t[i].r, r); #ifdef EOVERFLOW if (t[i].e && e != EOVERFLOW) printf ("Expected errno=EOVERFLOW=%d, got errno=%d\n", EOVERFLOW, e); #endif exit (1); } } mpfr_clear (x); } /* On 2023-03-22, on a 64-bit Linux machine (thus with 32-bit int), the case %.2147483648Rg yields an incorrect size computation and MPFR wants to allocate 18446744071562070545 bytes. With assertion checking (--enable-assert), one gets: vasprintf.c:1908: MPFR assertion failed: threshold >= 1 for the 2nd mpfr_snprintf below (the other calls with %.2147483648Rg have the same issue). This case should either succeed or fail as reaching an environmental limit like with glibc (note that the precision does not fit in an int). For MPFR, once this bug is fixed, this case should actually succeed, unless mpfr_intmax_t is a 32-bit type[*] (because 2147483648 is not representable in mpfr_intmax_t and in an int), so let's assume that in the tests below. [*] can be tested with -std=c90 -Werror -pedantic -Wformat -Wno-error=overlength-strings (this is a way to disable intmax_t). */ static void large_prec_for_g (void) { mpfr_t x; char buf1[4] = "xxx", buf2[4] = "xxx", buf3[4] = "xxx", buf4[4] = "xxx"; int allow_fail = (mpfr_uintmax_t) -1 == 0xffffffff; int r; mpfr_init2 (x, 128); mpfr_set_ui (x, 1, MPFR_RNDN); r = mpfr_snprintf (NULL, 0, "%.2147483647Rg\n", x); MPFR_ASSERTN (r == 2); r = mpfr_snprintf (NULL, 0, "%.2147483648Rg\n", x); MPFR_ASSERTN (r == 2 || (allow_fail && r < 0)); r = mpfr_snprintf (buf1, sizeof(buf1), "%.2147483647Rg\n", x); MPFR_ASSERTN (r == 2); MPFR_ASSERTN (buf1[0] == '1' && buf1[1] == '\n' && buf1[2] == 0); r = mpfr_snprintf (buf2, sizeof(buf2), "%.2147483648Rg\n", x); if (r < 0) MPFR_ASSERTN (allow_fail); else { MPFR_ASSERTN (r == 2); MPFR_ASSERTN (buf2[0] == '1' && buf2[1] == '\n' && buf2[2] == 0); } r = mpfr_sprintf (buf3, "%.2147483647Rg\n", x); MPFR_ASSERTN (r == 2); MPFR_ASSERTN (buf3[0] == '1' && buf3[1] == '\n' && buf3[2] == 0); r = mpfr_sprintf (buf4, "%.2147483648Rg\n", x); if (r < 0) MPFR_ASSERTN (allow_fail); else { MPFR_ASSERTN (r == 2); MPFR_ASSERTN (buf4[0] == '1' && buf4[1] == '\n' && buf4[2] == 0); } mpfr_clear (x); } #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) /* The following tests should be equivalent to those from test_locale() in tprintf.c (remove the \n at the end of the test strings). */ static void test_locale (void) { const char * const tab_locale[] = { "en_US", "en_US.iso88591", "en_US.iso885915", "en_US.utf8" }; int i; mpfr_t x; char v[] = "99999999999999999999999.5"; for (i = 0; i < numberof(tab_locale); i++) { char *s; s = setlocale (LC_ALL, tab_locale[i]); if (s != NULL && MPFR_THOUSANDS_SEPARATOR == ',') break; } if (i == numberof(tab_locale)) { if (getenv ("MPFR_CHECK_LOCALES") == NULL) return; fprintf (stderr, "Cannot find a locale with ',' thousands separator.\n" "Please install one of the en_US based locales.\n"); exit (1); } mpfr_init2 (x, 113); mpfr_set_ui (x, 10000, MPFR_RNDN); check_sprintf ("(1) 10000=10,000 ", "(1) 10000=%'Rg ", x); check_sprintf ("(2) 10000=10,000.000000 ", "(2) 10000=%'Rf ", x); mpfr_set_ui (x, 1000, MPFR_RNDN); check_sprintf ("(3) 1000=1,000.000000 ", "(3) 1000=%'Rf ", x); for (i = 1; i <= sizeof (v) - 3; i++) { char buf[64]; int j; strcpy (buf, "(4) 10^i=1"); for (j = i; j > 0; j--) strcat (buf, (j % 3 == 0) ? ",0" : "0"); strcat (buf, " "); mpfr_set_str (x, v + sizeof (v) - 3 - i, 10, MPFR_RNDN); check_sprintf (buf, "(4) 10^i=%'.0Rf ", x); } #define N0 20 for (i = 1; i <= N0; i++) { char s[N0+4], buf[64]; int j; s[0] = '1'; for (j = 1; j <= i; j++) s[j] = '0'; s[i+1] = '\0'; strcpy (buf, "(5) 10^i=1"); for (j = i; j > 0; j--) strcat (buf, (j % 3 == 0) ? ",0" : "0"); strcat (buf, " "); mpfr_set_str (x, s, 10, MPFR_RNDN); check_sprintf (buf, "(5) 10^i=%'.0RNf ", x); check_sprintf (buf, "(5) 10^i=%'.0RZf ", x); check_sprintf (buf, "(5) 10^i=%'.0RUf ", x); check_sprintf (buf, "(5) 10^i=%'.0RDf ", x); check_sprintf (buf, "(5) 10^i=%'.0RYf ", x); strcat (s + (i + 1), ".5"); check_sprintf (buf, "(5) 10^i=%'.0Rf ", x); } mpfr_set_str (x, "1000", 10, MPFR_RNDN); check_sprintf ("00000001e+03", "%'012.3Rg", x); check_sprintf ("00000001,000", "%'012.4Rg", x); check_sprintf ("000000001,000", "%'013.4Rg", x); #ifdef PRINTF_GROUPFLAG /* Do not test the thousands separator with a precision field larger than the number of digits (thus needing leading zeros), such as "%0+ -'13.10Pd:" (used up to MPFR 4.2.0), since the GNU libc is buggy: https://sourceware.org/bugzilla/show_bug.cgi?id=23432 We don't know about the other implementations. This new test works fine with glibc up to 2.36, but fails with 2.37 (as reported by Klaus Dittrich in the MPFR mailing-list); this is actually a bug introduced in glibc 2.37, not in MPFR: https://sourceware.org/bugzilla/show_bug.cgi?id=30068 Since this bug can yield a buffer overflow (CVE-2023-25139), possibly affecting MPFR users, let us rather require a fix in glibc. This bug has been fixed in the 2.37 branch: https://sourceware.org/git/?p=glibc.git;a=commit;h=07b9521fc6 If we wanted to check that and avoid a failure of the test because of a buggy C library (while MPFR would be consistent with the C library), we could compare the MPFR output with both the correct output and the output from the C library (possibly buggy). But to do that in a clean way, this would require a change in the check_vsprintf() call. */ check_vsprintf ("+1,234,567 :", "%0+ -'13Pd:", (mpfr_prec_t) 1234567); #endif mpfr_clear (x); } #else static void test_locale (void) { if (getenv ("MPFR_CHECK_LOCALES") != NULL) { fprintf (stderr, "Cannot test locales.\n"); exit (1); } } #endif int main (int argc, char **argv) { int k; tests_start_mpfr (); #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) /* currently, we just check with 'C' locale */ setlocale (LC_ALL, "C"); #endif bug20111102 (); for (k = 0; k < 40; k++) { native_types (); special (); decimal (); hexadecimal (); binary (); #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) && MPFR_LCONV_DPTS locale_da_DK (); #else if (getenv ("MPFR_CHECK_LOCALES") != NULL) { fprintf (stderr, "Cannot test locales.\n"); exit (1); } #endif } check_emax (); check_emin (); test20161214 (); bug21056 (); snprintf_size (); percent_n (); mixed (); check_length_overflow (); large_prec_for_g (); test_locale (); if (getenv ("MPFR_CHECK_LIBC_PRINTF")) { /* check against libc */ random_double (); bug20081214 (); bug20080610 (); } tests_end_mpfr (); return 0; } #else /* HAVE_STDARG */ int main (void) { /* We have nothing to test. */ return 77; } #endif /* HAVE_STDARG */