| /* Test gmp_printf and related functions. |
| |
| Copyright 2001-2003, 2015 Free Software Foundation, Inc. |
| |
| This file is part of the GNU MP Library test suite. |
| |
| The GNU MP Library test suite is free software; you can redistribute it |
| and/or modify it under the terms of the GNU 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 MP Library test suite 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 General |
| Public License for more details. |
| |
| You should have received a copy of the GNU General Public License along with |
| the GNU MP Library test suite. If not, see https://www.gnu.org/licenses/. */ |
| |
| |
| /* Usage: t-printf [-s] |
| |
| -s Check the data against the system printf, where possible. This is |
| only an option since we don't want to fail if the system printf is |
| faulty or strange. */ |
| |
| |
| #include "config.h" /* needed for the HAVE_, could also move gmp incls */ |
| |
| #include <stdarg.h> |
| #include <stddef.h> /* for ptrdiff_t */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #if HAVE_OBSTACK_VPRINTF |
| #define obstack_chunk_alloc tests_allocate |
| #define obstack_chunk_free tests_free_nosize |
| #include <obstack.h> |
| #endif |
| |
| #if HAVE_INTTYPES_H |
| # include <inttypes.h> /* for intmax_t */ |
| #else |
| # if HAVE_STDINT_H |
| # include <stdint.h> |
| # endif |
| #endif |
| |
| #if HAVE_UNISTD_H |
| #include <unistd.h> /* for unlink */ |
| #endif |
| |
| #include "gmp-impl.h" |
| #include "tests.h" |
| |
| |
| int option_check_printf = 0; |
| |
| |
| #define CHECK_VFPRINTF_FILENAME "t-printf.tmp" |
| FILE *check_vfprintf_fp; |
| |
| |
| /* From any of the tests run here. */ |
| #define MAX_OUTPUT 1024 |
| |
| |
| void |
| check_plain (const char *want, const char *fmt_orig, ...) |
| { |
| char got[MAX_OUTPUT]; |
| int got_len, want_len; |
| size_t fmtsize; |
| char *fmt, *q; |
| const char *p; |
| va_list ap; |
| va_start (ap, fmt_orig); |
| |
| if (! option_check_printf) |
| return; |
| |
| fmtsize = strlen (fmt_orig) + 1; |
| fmt = (char *) (*__gmp_allocate_func) (fmtsize); |
| |
| for (p = fmt_orig, q = fmt; *p != '\0'; p++) |
| { |
| switch (*p) { |
| case 'a': |
| case 'A': |
| /* The exact value of the exponent isn't guaranteed in glibc, and it |
| and gmp_printf do slightly different things, so don't compare |
| directly. */ |
| goto done; |
| case 'F': |
| if (p > fmt_orig && *(p-1) == '.') |
| goto done; /* don't test the "all digits" cases */ |
| /* discard 'F' type */ |
| break; |
| case 'Z': |
| /* transmute */ |
| *q++ = 'l'; |
| break; |
| default: |
| *q++ = *p; |
| break; |
| } |
| } |
| *q = '\0'; |
| |
| want_len = strlen (want); |
| ASSERT_ALWAYS (want_len < sizeof(got)); |
| |
| got_len = vsprintf (got, fmt, ap); |
| |
| if (got_len != want_len || strcmp (got, want) != 0) |
| { |
| printf ("wanted data doesn't match plain vsprintf\n"); |
| printf (" fmt |%s|\n", fmt); |
| printf (" got |%s|\n", got); |
| printf (" want |%s|\n", want); |
| printf (" got_len %d\n", got_len); |
| printf (" want_len %d\n", want_len); |
| abort (); |
| } |
| |
| done: |
| (*__gmp_free_func) (fmt, fmtsize); |
| } |
| |
| void |
| check_vsprintf (const char *want, const char *fmt, va_list ap) |
| { |
| char got[MAX_OUTPUT]; |
| int got_len, want_len; |
| |
| want_len = strlen (want); |
| got_len = gmp_vsprintf (got, fmt, ap); |
| |
| if (got_len != want_len || strcmp (got, want) != 0) |
| { |
| printf ("gmp_vsprintf wrong\n"); |
| printf (" fmt |%s|\n", fmt); |
| printf (" got |%s|\n", got); |
| printf (" want |%s|\n", want); |
| printf (" got_len %d\n", got_len); |
| printf (" want_len %d\n", want_len); |
| abort (); |
| } |
| } |
| |
| void |
| check_vfprintf (const char *want, const char *fmt, va_list ap) |
| { |
| char got[MAX_OUTPUT]; |
| int got_len, want_len, fread_len; |
| long ftell_len; |
| |
| want_len = strlen (want); |
| |
| rewind (check_vfprintf_fp); |
| got_len = gmp_vfprintf (check_vfprintf_fp, fmt, ap); |
| ASSERT_ALWAYS (got_len != -1); |
| ASSERT_ALWAYS (fflush (check_vfprintf_fp) == 0); |
| |
| ftell_len = ftell (check_vfprintf_fp); |
| ASSERT_ALWAYS (ftell_len != -1); |
| |
| rewind (check_vfprintf_fp); |
| ASSERT_ALWAYS (ftell_len <= sizeof(got)); |
| fread_len = fread (got, 1, ftell_len, check_vfprintf_fp); |
| |
| if (got_len != want_len |
| || ftell_len != want_len |
| || fread_len != want_len |
| || memcmp (got, want, want_len) != 0) |
| { |
| printf ("gmp_vfprintf wrong\n"); |
| printf (" fmt |%s|\n", fmt); |
| printf (" got |%.*s|\n", fread_len, got); |
| printf (" want |%s|\n", want); |
| printf (" got_len %d\n", got_len); |
| printf (" ftell_len %ld\n", ftell_len); |
| printf (" fread_len %d\n", fread_len); |
| printf (" want_len %d\n", want_len); |
| abort (); |
| } |
| } |
| |
| void |
| check_vsnprintf (const char *want, const char *fmt, va_list ap) |
| { |
| char got[MAX_OUTPUT+1]; |
| int ret, got_len, want_len; |
| size_t bufsize; |
| |
| want_len = strlen (want); |
| |
| bufsize = -1; |
| for (;;) |
| { |
| /* do 0 to 5, then want-5 to want+5 */ |
| bufsize++; |
| if (bufsize > 5 && bufsize < want_len-5) |
| bufsize = want_len-5; |
| if (bufsize > want_len + 5) |
| break; |
| ASSERT_ALWAYS (bufsize+1 <= sizeof (got)); |
| |
| got[bufsize] = '!'; |
| ret = gmp_vsnprintf (got, bufsize, fmt, ap); |
| |
| got_len = MIN (MAX(1,bufsize)-1, want_len); |
| |
| if (got[bufsize] != '!') |
| { |
| printf ("gmp_vsnprintf overwrote bufsize sentinel\n"); |
| goto error; |
| } |
| |
| if (ret != want_len) |
| { |
| printf ("gmp_vsnprintf return value wrong\n"); |
| goto error; |
| } |
| |
| if (bufsize > 0) |
| { |
| if (memcmp (got, want, got_len) != 0 || got[got_len] != '\0') |
| { |
| printf ("gmp_vsnprintf wrong result string\n"); |
| error: |
| printf (" fmt |%s|\n", fmt); |
| printf (" bufsize %lu\n", (unsigned long) bufsize); |
| printf (" got |%s|\n", got); |
| printf (" want |%.*s|\n", got_len, want); |
| printf (" want full |%s|\n", want); |
| printf (" ret %d\n", ret); |
| printf (" want_len %d\n", want_len); |
| abort (); |
| } |
| } |
| } |
| } |
| |
| void |
| check_vasprintf (const char *want, const char *fmt, va_list ap) |
| { |
| char *got; |
| int got_len, want_len; |
| |
| want_len = strlen (want); |
| got_len = gmp_vasprintf (&got, fmt, ap); |
| |
| if (got_len != want_len || strcmp (got, want) != 0) |
| { |
| printf ("gmp_vasprintf wrong\n"); |
| printf (" fmt |%s|\n", fmt); |
| printf (" got |%s|\n", got); |
| printf (" want |%s|\n", want); |
| printf (" got_len %d\n", got_len); |
| printf (" want_len %d\n", want_len); |
| abort (); |
| } |
| (*__gmp_free_func) (got, strlen(got)+1); |
| } |
| |
| void |
| check_obstack_vprintf (const char *want, const char *fmt, va_list ap) |
| { |
| #if HAVE_OBSTACK_VPRINTF |
| struct obstack ob; |
| int got_len, want_len, ob_len; |
| char *got; |
| |
| want_len = strlen (want); |
| |
| obstack_init (&ob); |
| got_len = gmp_obstack_vprintf (&ob, fmt, ap); |
| got = (char *) obstack_base (&ob); |
| ob_len = obstack_object_size (&ob); |
| |
| if (got_len != want_len |
| || ob_len != want_len |
| || memcmp (got, want, want_len) != 0) |
| { |
| printf ("gmp_obstack_vprintf wrong\n"); |
| printf (" fmt |%s|\n", fmt); |
| printf (" got |%s|\n", got); |
| printf (" want |%s|\n", want); |
| printf (" got_len %d\n", got_len); |
| printf (" ob_len %d\n", ob_len); |
| printf (" want_len %d\n", want_len); |
| abort (); |
| } |
| obstack_free (&ob, NULL); |
| #endif |
| } |
| |
| |
| void |
| check_one (const char *want, const char *fmt, ...) |
| { |
| va_list ap; |
| va_start (ap, fmt); |
| |
| /* simplest first */ |
| check_vsprintf (want, fmt, ap); |
| check_vfprintf (want, fmt, ap); |
| check_vsnprintf (want, fmt, ap); |
| check_vasprintf (want, fmt, ap); |
| check_obstack_vprintf (want, fmt, ap); |
| } |
| |
| |
| #define hex_or_octal_p(fmt) \ |
| (strchr (fmt, 'x') != NULL \ |
| || strchr (fmt, 'X') != NULL \ |
| || strchr (fmt, 'o') != NULL) |
| |
| void |
| check_z (void) |
| { |
| static const struct { |
| const char *fmt; |
| const char *z; |
| const char *want; |
| } data[] = { |
| { "%Zd", "0", "0" }, |
| { "%Zd", "1", "1" }, |
| { "%Zd", "123", "123" }, |
| { "%Zd", "-1", "-1" }, |
| { "%Zd", "-123", "-123" }, |
| |
| { "%+Zd", "0", "+0" }, |
| { "%+Zd", "123", "+123" }, |
| { "%+Zd", "-123", "-123" }, |
| |
| { "%Zx", "123", "7b" }, |
| { "%ZX", "123", "7B" }, |
| { "%Zx", "-123", "-7b" }, |
| { "%ZX", "-123", "-7B" }, |
| { "%Zo", "123", "173" }, |
| { "%Zo", "-123", "-173" }, |
| |
| { "%#Zx", "0", "0" }, |
| { "%#ZX", "0", "0" }, |
| { "%#Zx", "123", "0x7b" }, |
| { "%#ZX", "123", "0X7B" }, |
| { "%#Zx", "-123", "-0x7b" }, |
| { "%#ZX", "-123", "-0X7B" }, |
| |
| { "%#Zo", "0", "0" }, |
| { "%#Zo", "123", "0173" }, |
| { "%#Zo", "-123", "-0173" }, |
| |
| { "%10Zd", "0", " 0" }, |
| { "%10Zd", "123", " 123" }, |
| { "%10Zd", "-123", " -123" }, |
| |
| { "%-10Zd", "0", "0 " }, |
| { "%-10Zd", "123", "123 " }, |
| { "%-10Zd", "-123", "-123 " }, |
| |
| { "%+10Zd", "123", " +123" }, |
| { "%+-10Zd", "123", "+123 " }, |
| { "%+10Zd", "-123", " -123" }, |
| { "%+-10Zd", "-123", "-123 " }, |
| |
| { "%08Zd", "0", "00000000" }, |
| { "%08Zd", "123", "00000123" }, |
| { "%08Zd", "-123", "-0000123" }, |
| |
| { "%+08Zd", "0", "+0000000" }, |
| { "%+08Zd", "123", "+0000123" }, |
| { "%+08Zd", "-123", "-0000123" }, |
| |
| { "%#08Zx", "0", "00000000" }, |
| { "%#08Zx", "123", "0x00007b" }, |
| { "%#08Zx", "-123", "-0x0007b" }, |
| |
| { "%+#08Zx", "0", "+0000000" }, |
| { "%+#08Zx", "123", "+0x0007b" }, |
| { "%+#08Zx", "-123", "-0x0007b" }, |
| |
| { "%.0Zd", "0", "" }, |
| { "%.1Zd", "0", "0" }, |
| { "%.2Zd", "0", "00" }, |
| { "%.3Zd", "0", "000" }, |
| }; |
| |
| int i, j; |
| mpz_t z; |
| char *nfmt; |
| mp_size_t nsize, zeros; |
| |
| mpz_init (z); |
| |
| for (i = 0; i < numberof (data); i++) |
| { |
| mpz_set_str_or_abort (z, data[i].z, 0); |
| |
| /* don't try negatives or forced sign in hex or octal */ |
| if (mpz_fits_slong_p (z) |
| && ! (hex_or_octal_p (data[i].fmt) |
| && (strchr (data[i].fmt, '+') != NULL || mpz_sgn(z) < 0))) |
| { |
| check_plain (data[i].want, data[i].fmt, mpz_get_si (z)); |
| } |
| |
| check_one (data[i].want, data[i].fmt, z); |
| |
| /* Same again, with %N and possibly some high zero limbs */ |
| nfmt = __gmp_allocate_strdup (data[i].fmt); |
| for (j = 0; nfmt[j] != '\0'; j++) |
| if (nfmt[j] == 'Z') |
| nfmt[j] = 'N'; |
| for (zeros = 0; zeros <= 3; zeros++) |
| { |
| nsize = ABSIZ(z)+zeros; |
| MPZ_REALLOC (z, nsize); |
| nsize = (SIZ(z) >= 0 ? nsize : -nsize); |
| refmpn_zero (PTR(z)+ABSIZ(z), zeros); |
| check_one (data[i].want, nfmt, PTR(z), nsize); |
| } |
| __gmp_free_func (nfmt, strlen(nfmt)+1); |
| } |
| |
| mpz_clear (z); |
| } |
| |
| void |
| check_q (void) |
| { |
| static const struct { |
| const char *fmt; |
| const char *q; |
| const char *want; |
| } data[] = { |
| { "%Qd", "0", "0" }, |
| { "%Qd", "1", "1" }, |
| { "%Qd", "123", "123" }, |
| { "%Qd", "-1", "-1" }, |
| { "%Qd", "-123", "-123" }, |
| { "%Qd", "3/2", "3/2" }, |
| { "%Qd", "-3/2", "-3/2" }, |
| |
| { "%+Qd", "0", "+0" }, |
| { "%+Qd", "123", "+123" }, |
| { "%+Qd", "-123", "-123" }, |
| { "%+Qd", "5/8", "+5/8" }, |
| { "%+Qd", "-5/8", "-5/8" }, |
| |
| { "%Qx", "123", "7b" }, |
| { "%QX", "123", "7B" }, |
| { "%Qx", "15/16", "f/10" }, |
| { "%QX", "15/16", "F/10" }, |
| { "%Qx", "-123", "-7b" }, |
| { "%QX", "-123", "-7B" }, |
| { "%Qx", "-15/16", "-f/10" }, |
| { "%QX", "-15/16", "-F/10" }, |
| { "%Qo", "123", "173" }, |
| { "%Qo", "-123", "-173" }, |
| { "%Qo", "16/17", "20/21" }, |
| { "%Qo", "-16/17", "-20/21" }, |
| |
| { "%#Qx", "0", "0" }, |
| { "%#QX", "0", "0" }, |
| { "%#Qx", "123", "0x7b" }, |
| { "%#QX", "123", "0X7B" }, |
| { "%#Qx", "5/8", "0x5/0x8" }, |
| { "%#QX", "5/8", "0X5/0X8" }, |
| { "%#Qx", "-123", "-0x7b" }, |
| { "%#QX", "-123", "-0X7B" }, |
| { "%#Qx", "-5/8", "-0x5/0x8" }, |
| { "%#QX", "-5/8", "-0X5/0X8" }, |
| { "%#Qo", "0", "0" }, |
| { "%#Qo", "123", "0173" }, |
| { "%#Qo", "-123", "-0173" }, |
| { "%#Qo", "5/7", "05/07" }, |
| { "%#Qo", "-5/7", "-05/07" }, |
| |
| /* zero denominator and showbase */ |
| { "%#10Qo", "0/0", " 0/0" }, |
| { "%#10Qd", "0/0", " 0/0" }, |
| { "%#10Qx", "0/0", " 0/0" }, |
| { "%#10Qo", "123/0", " 0173/0" }, |
| { "%#10Qd", "123/0", " 123/0" }, |
| { "%#10Qx", "123/0", " 0x7b/0" }, |
| { "%#10QX", "123/0", " 0X7B/0" }, |
| { "%#10Qo", "-123/0", " -0173/0" }, |
| { "%#10Qd", "-123/0", " -123/0" }, |
| { "%#10Qx", "-123/0", " -0x7b/0" }, |
| { "%#10QX", "-123/0", " -0X7B/0" }, |
| |
| { "%10Qd", "0", " 0" }, |
| { "%-10Qd", "0", "0 " }, |
| { "%10Qd", "123", " 123" }, |
| { "%-10Qd", "123", "123 " }, |
| { "%10Qd", "-123", " -123" }, |
| { "%-10Qd", "-123", "-123 " }, |
| |
| { "%+10Qd", "123", " +123" }, |
| { "%+-10Qd", "123", "+123 " }, |
| { "%+10Qd", "-123", " -123" }, |
| { "%+-10Qd", "-123", "-123 " }, |
| |
| { "%08Qd", "0", "00000000" }, |
| { "%08Qd", "123", "00000123" }, |
| { "%08Qd", "-123", "-0000123" }, |
| |
| { "%+08Qd", "0", "+0000000" }, |
| { "%+08Qd", "123", "+0000123" }, |
| { "%+08Qd", "-123", "-0000123" }, |
| |
| { "%#08Qx", "0", "00000000" }, |
| { "%#08Qx", "123", "0x00007b" }, |
| { "%#08Qx", "-123", "-0x0007b" }, |
| |
| { "%+#08Qx", "0", "+0000000" }, |
| { "%+#08Qx", "123", "+0x0007b" }, |
| { "%+#08Qx", "-123", "-0x0007b" }, |
| }; |
| |
| int i; |
| mpq_t q; |
| |
| mpq_init (q); |
| |
| for (i = 0; i < numberof (data); i++) |
| { |
| mpq_set_str_or_abort (q, data[i].q, 0); |
| check_one (data[i].want, data[i].fmt, q); |
| } |
| |
| mpq_clear (q); |
| } |
| |
| void |
| check_f (void) |
| { |
| static const struct { |
| const char *fmt; |
| const char *f; |
| const char *want; |
| |
| } data[] = { |
| |
| { "%Ff", "0", "0.000000" }, |
| { "%Ff", "123", "123.000000" }, |
| { "%Ff", "-123", "-123.000000" }, |
| |
| { "%+Ff", "0", "+0.000000" }, |
| { "%+Ff", "123", "+123.000000" }, |
| { "%+Ff", "-123", "-123.000000" }, |
| |
| { "%.0Ff", "0", "0" }, |
| { "%.0Ff", "123", "123" }, |
| { "%.0Ff", "-123", "-123" }, |
| |
| { "%8.0Ff", "0", " 0" }, |
| { "%8.0Ff", "123", " 123" }, |
| { "%8.0Ff", "-123", " -123" }, |
| |
| { "%08.0Ff", "0", "00000000" }, |
| { "%08.0Ff", "123", "00000123" }, |
| { "%08.0Ff", "-123", "-0000123" }, |
| |
| { "%10.2Ff", "0", " 0.00" }, |
| { "%10.2Ff", "0.25", " 0.25" }, |
| { "%10.2Ff", "123.25", " 123.25" }, |
| { "%10.2Ff", "-123.25", " -123.25" }, |
| |
| { "%-10.2Ff", "0", "0.00 " }, |
| { "%-10.2Ff", "0.25", "0.25 " }, |
| { "%-10.2Ff", "123.25", "123.25 " }, |
| { "%-10.2Ff", "-123.25", "-123.25 " }, |
| |
| { "%.2Ff", "0.00000000000001", "0.00" }, |
| { "%.2Ff", "0.002", "0.00" }, |
| { "%.2Ff", "0.008", "0.01" }, |
| |
| { "%.0Ff", "123.00000000000001", "123" }, |
| { "%.0Ff", "123.2", "123" }, |
| { "%.0Ff", "123.8", "124" }, |
| |
| { "%.0Ff", "999999.9", "1000000" }, |
| { "%.0Ff", "3999999.9", "4000000" }, |
| |
| { "%Fe", "0", "0.000000e+00" }, |
| { "%Fe", "1", "1.000000e+00" }, |
| { "%Fe", "123", "1.230000e+02" }, |
| |
| { "%FE", "0", "0.000000E+00" }, |
| { "%FE", "1", "1.000000E+00" }, |
| { "%FE", "123", "1.230000E+02" }, |
| |
| { "%Fe", "0", "0.000000e+00" }, |
| { "%Fe", "1", "1.000000e+00" }, |
| |
| { "%.0Fe", "10000000000", "1e+10" }, |
| { "%.0Fe", "-10000000000", "-1e+10" }, |
| |
| { "%.2Fe", "10000000000", "1.00e+10" }, |
| { "%.2Fe", "-10000000000", "-1.00e+10" }, |
| |
| { "%8.0Fe", "10000000000", " 1e+10" }, |
| { "%8.0Fe", "-10000000000", " -1e+10" }, |
| |
| { "%-8.0Fe", "10000000000", "1e+10 " }, |
| { "%-8.0Fe", "-10000000000", "-1e+10 " }, |
| |
| { "%12.2Fe", "10000000000", " 1.00e+10" }, |
| { "%12.2Fe", "-10000000000", " -1.00e+10" }, |
| |
| { "%012.2Fe", "10000000000", "00001.00e+10" }, |
| { "%012.2Fe", "-10000000000", "-0001.00e+10" }, |
| |
| { "%Fg", "0", "0" }, |
| { "%Fg", "1", "1" }, |
| { "%Fg", "-1", "-1" }, |
| |
| { "%.0Fg", "0", "0" }, |
| { "%.0Fg", "1", "1" }, |
| { "%.0Fg", "-1", "-1" }, |
| |
| { "%.1Fg", "100", "1e+02" }, |
| { "%.2Fg", "100", "1e+02" }, |
| { "%.3Fg", "100", "100" }, |
| { "%.4Fg", "100", "100" }, |
| |
| { "%Fg", "0.001", "0.001" }, |
| { "%Fg", "0.0001", "0.0001" }, |
| { "%Fg", "0.00001", "1e-05" }, |
| { "%Fg", "0.000001", "1e-06" }, |
| |
| { "%.4Fg", "1.00000000000001", "1" }, |
| { "%.4Fg", "100000000000001", "1e+14" }, |
| |
| { "%.4Fg", "12345678", "1.235e+07" }, |
| |
| { "%Fa", "0","0x0p+0" }, |
| { "%FA", "0","0X0P+0" }, |
| |
| { "%Fa", "1","0x1p+0" }, |
| { "%Fa", "65535","0xf.fffp+12" }, |
| { "%Fa", "65536","0x1p+16" }, |
| { "%F.10a", "65536","0x1.0000000000p+16" }, |
| { "%F.1a", "65535","0x1.0p+16" }, |
| { "%F.0a", "65535","0x1p+16" }, |
| |
| { "%.2Ff", "0.99609375", "1.00" }, |
| { "%.Ff", "0.99609375", "0.99609375" }, |
| { "%.Fe", "0.99609375", "9.9609375e-01" }, |
| { "%.Fg", "0.99609375", "0.99609375" }, |
| { "%.20Fg", "1000000", "1000000" }, |
| { "%.Fg", "1000000", "1000000" }, |
| |
| { "%#.0Ff", "1", "1." }, |
| { "%#.0Fe", "1", "1.e+00" }, |
| { "%#.0Fg", "1", "1." }, |
| |
| { "%#.1Ff", "1", "1.0" }, |
| { "%#.1Fe", "1", "1.0e+00" }, |
| { "%#.1Fg", "1", "1." }, |
| |
| { "%#.4Ff", "1234", "1234.0000" }, |
| { "%#.4Fe", "1234", "1.2340e+03" }, |
| { "%#.4Fg", "1234", "1234." }, |
| |
| { "%#.8Ff", "1234", "1234.00000000" }, |
| { "%#.8Fe", "1234", "1.23400000e+03" }, |
| { "%#.8Fg", "1234", "1234.0000" }, |
| |
| }; |
| |
| int i; |
| mpf_t f; |
| double d; |
| |
| mpf_init2 (f, 256L); |
| |
| for (i = 0; i < numberof (data); i++) |
| { |
| if (data[i].f[0] == '0' && data[i].f[1] == 'x') |
| mpf_set_str_or_abort (f, data[i].f, 16); |
| else |
| mpf_set_str_or_abort (f, data[i].f, 10); |
| |
| /* if mpf->double doesn't truncate, then expect same result */ |
| d = mpf_get_d (f); |
| if (mpf_cmp_d (f, d) == 0) |
| check_plain (data[i].want, data[i].fmt, d); |
| |
| check_one (data[i].want, data[i].fmt, f); |
| } |
| |
| mpf_clear (f); |
| } |
| |
| |
| void |
| check_limb (void) |
| { |
| int i; |
| mp_limb_t limb; |
| mpz_t z; |
| char *s; |
| |
| check_one ("0", "%Md", CNST_LIMB(0)); |
| check_one ("1", "%Md", CNST_LIMB(1)); |
| |
| /* "i" many 1 bits, tested against mpz_get_str in decimal and hex */ |
| limb = 1; |
| mpz_init_set_ui (z, 1L); |
| for (i = 1; i <= GMP_LIMB_BITS; i++) |
| { |
| s = mpz_get_str (NULL, 10, z); |
| check_one (s, "%Mu", limb); |
| (*__gmp_free_func) (s, strlen (s) + 1); |
| |
| s = mpz_get_str (NULL, 16, z); |
| check_one (s, "%Mx", limb); |
| (*__gmp_free_func) (s, strlen (s) + 1); |
| |
| s = mpz_get_str (NULL, -16, z); |
| check_one (s, "%MX", limb); |
| (*__gmp_free_func) (s, strlen (s) + 1); |
| |
| limb = 2*limb + 1; |
| mpz_mul_2exp (z, z, 1L); |
| mpz_add_ui (z, z, 1L); |
| } |
| |
| mpz_clear (z); |
| } |
| |
| |
| void |
| check_n (void) |
| { |
| { |
| int n = -1; |
| check_one ("blah", "%nblah", &n); |
| ASSERT_ALWAYS (n == 0); |
| } |
| |
| { |
| int n = -1; |
| check_one ("hello ", "hello %n", &n); |
| ASSERT_ALWAYS (n == 6); |
| } |
| |
| { |
| int n = -1; |
| check_one ("hello world", "hello %n world", &n); |
| ASSERT_ALWAYS (n == 6); |
| } |
| |
| #define CHECK_N(type, string) \ |
| do { \ |
| type x[2]; \ |
| char fmt[128]; \ |
| \ |
| x[0] = ~ (type) 0; \ |
| x[1] = ~ (type) 0; \ |
| sprintf (fmt, "%%d%%%sn%%d", string); \ |
| check_one ("123456", fmt, 123, &x[0], 456); \ |
| \ |
| /* should write whole of x[0] and none of x[1] */ \ |
| ASSERT_ALWAYS (x[0] == 3); \ |
| ASSERT_ALWAYS (x[1] == (type) ~ (type) 0); \ |
| \ |
| } while (0) |
| |
| CHECK_N (mp_limb_t, "M"); |
| CHECK_N (char, "hh"); |
| CHECK_N (long, "l"); |
| #if HAVE_LONG_LONG |
| CHECK_N (long long, "L"); |
| #endif |
| #if HAVE_INTMAX_T |
| CHECK_N (intmax_t, "j"); |
| #endif |
| #if HAVE_PTRDIFF_T |
| CHECK_N (ptrdiff_t, "t"); |
| #endif |
| CHECK_N (short, "h"); |
| CHECK_N (size_t, "z"); |
| |
| { |
| mpz_t x[2]; |
| mpz_init_set_si (x[0], -987L); |
| mpz_init_set_si (x[1], 654L); |
| check_one ("123456", "%d%Zn%d", 123, x[0], 456); |
| MPZ_CHECK_FORMAT (x[0]); |
| MPZ_CHECK_FORMAT (x[1]); |
| ASSERT_ALWAYS (mpz_cmp_ui (x[0], 3L) == 0); |
| ASSERT_ALWAYS (mpz_cmp_ui (x[1], 654L) == 0); |
| mpz_clear (x[0]); |
| mpz_clear (x[1]); |
| } |
| |
| { |
| mpq_t x[2]; |
| mpq_init (x[0]); |
| mpq_init (x[1]); |
| mpq_set_ui (x[0], 987L, 654L); |
| mpq_set_ui (x[1], 4115L, 226L); |
| check_one ("123456", "%d%Qn%d", 123, x[0], 456); |
| MPQ_CHECK_FORMAT (x[0]); |
| MPQ_CHECK_FORMAT (x[1]); |
| ASSERT_ALWAYS (mpq_cmp_ui (x[0], 3L, 1L) == 0); |
| ASSERT_ALWAYS (mpq_cmp_ui (x[1], 4115L, 226L) == 0); |
| mpq_clear (x[0]); |
| mpq_clear (x[1]); |
| } |
| |
| { |
| mpf_t x[2]; |
| mpf_init (x[0]); |
| mpf_init (x[1]); |
| mpf_set_ui (x[0], 987L); |
| mpf_set_ui (x[1], 654L); |
| check_one ("123456", "%d%Fn%d", 123, x[0], 456); |
| MPF_CHECK_FORMAT (x[0]); |
| MPF_CHECK_FORMAT (x[1]); |
| ASSERT_ALWAYS (mpf_cmp_ui (x[0], 3L) == 0); |
| ASSERT_ALWAYS (mpf_cmp_ui (x[1], 654L) == 0); |
| mpf_clear (x[0]); |
| mpf_clear (x[1]); |
| } |
| |
| { |
| mp_limb_t a[5]; |
| mp_limb_t a_want[numberof(a)]; |
| mp_size_t i; |
| |
| a[0] = 123; |
| check_one ("blah", "bl%Nnah", a, (mp_size_t) 0); |
| ASSERT_ALWAYS (a[0] == 123); |
| |
| MPN_ZERO (a_want, numberof (a_want)); |
| for (i = 1; i < numberof (a); i++) |
| { |
| check_one ("blah", "bl%Nnah", a, i); |
| a_want[0] = 2; |
| ASSERT_ALWAYS (mpn_cmp (a, a_want, i) == 0); |
| } |
| } |
| } |
| |
| |
| void |
| check_misc (void) |
| { |
| mpz_t z; |
| mpf_t f; |
| |
| mpz_init (z); |
| mpf_init2 (f, 128L); |
| |
| check_one ("!", "%c", '!'); |
| |
| check_one ("hello world", "hello %s", "world"); |
| check_one ("hello:", "%s:", "hello"); |
| mpz_set_ui (z, 0L); |
| check_one ("hello0", "%s%Zd", "hello", z, z); |
| |
| { |
| static char xs[801]; |
| memset (xs, 'x', sizeof(xs)-1); |
| check_one (xs, "%s", xs); |
| } |
| { |
| char *xs; |
| xs = (char *) (*__gmp_allocate_func) (MAX_OUTPUT * 2 - 12); |
| memset (xs, '%', MAX_OUTPUT * 2 - 14); |
| xs [MAX_OUTPUT * 2 - 13] = '\0'; |
| xs [MAX_OUTPUT * 2 - 14] = 'x'; |
| check_one (xs + MAX_OUTPUT - 7, xs, NULL); |
| (*__gmp_free_func) (xs, MAX_OUTPUT * 2 - 12); |
| } |
| |
| mpz_set_ui (z, 12345L); |
| check_one (" 12345", "%*Zd", 10, z); |
| check_one ("0000012345", "%0*Zd", 10, z); |
| check_one ("12345 ", "%*Zd", -10, z); |
| check_one ("12345 and 678", "%Zd and %d", z, 678); |
| check_one ("12345,1,12345,2,12345", "%Zd,%d,%Zd,%d,%Zd", z, 1, z, 2, z); |
| |
| /* from the glibc info docs */ |
| mpz_set_si (z, 0L); |
| check_one ("| 0|0 | +0|+0 | 0|00000| | 00|0|", |
| "|%5Zd|%-5Zd|%+5Zd|%+-5Zd|% 5Zd|%05Zd|%5.0Zd|%5.2Zd|%Zd|", |
| /**/ z, z, z, z, z, z, z, z, z); |
| mpz_set_si (z, 1L); |
| check_one ("| 1|1 | +1|+1 | 1|00001| 1| 01|1|", |
| "|%5Zd|%-5Zd|%+5Zd|%+-5Zd|% 5Zd|%05Zd|%5.0Zd|%5.2Zd|%Zd|", |
| /**/ z, z, z, z, z, z, z, z, z); |
| mpz_set_si (z, -1L); |
| check_one ("| -1|-1 | -1|-1 | -1|-0001| -1| -01|-1|", |
| "|%5Zd|%-5Zd|%+5Zd|%+-5Zd|% 5Zd|%05Zd|%5.0Zd|%5.2Zd|%Zd|", |
| /**/ z, z, z, z, z, z, z, z, z); |
| mpz_set_si (z, 100000L); |
| check_one ("|100000|100000|+100000|+100000| 100000|100000|100000|100000|100000|", |
| "|%5Zd|%-5Zd|%+5Zd|%+-5Zd|% 5Zd|%05Zd|%5.0Zd|%5.2Zd|%Zd|", |
| /**/ z, z, z, z, z, z, z, z, z); |
| mpz_set_si (z, 0L); |
| check_one ("| 0| 0| 0| 0| 0| 0| 00000000|", |
| "|%5Zo|%5Zx|%5ZX|%#5Zo|%#5Zx|%#5ZX|%#10.8Zx|", |
| /**/ z, z, z, z, z, z, z); |
| mpz_set_si (z, 1L); |
| check_one ("| 1| 1| 1| 01| 0x1| 0X1|0x00000001|", |
| "|%5Zo|%5Zx|%5ZX|%#5Zo|%#5Zx|%#5ZX|%#10.8Zx|", |
| /**/ z, z, z, z, z, z, z); |
| mpz_set_si (z, 100000L); |
| check_one ("|303240|186a0|186A0|0303240|0x186a0|0X186A0|0x000186a0|", |
| "|%5Zo|%5Zx|%5ZX|%#5Zo|%#5Zx|%#5ZX|%#10.8Zx|", |
| /**/ z, z, z, z, z, z, z); |
| |
| /* %zd for size_t won't be available on old systems, and running something |
| to see if it works might be bad, so only try it on glibc, and only on a |
| new enough version (glibc 2.0 doesn't have %zd) */ |
| #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 0) |
| mpz_set_ui (z, 789L); |
| check_one ("456 789 blah", "%zd %Zd blah", (size_t) 456, z); |
| #endif |
| |
| mpz_clear (z); |
| mpf_clear (f); |
| } |
| |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| if (argc > 1 && strcmp (argv[1], "-s") == 0) |
| option_check_printf = 1; |
| |
| tests_start (); |
| check_vfprintf_fp = fopen (CHECK_VFPRINTF_FILENAME, "w+"); |
| ASSERT_ALWAYS (check_vfprintf_fp != NULL); |
| |
| check_z (); |
| check_q (); |
| check_f (); |
| check_limb (); |
| check_n (); |
| check_misc (); |
| |
| ASSERT_ALWAYS (fclose (check_vfprintf_fp) == 0); |
| unlink (CHECK_VFPRINTF_FILENAME); |
| tests_end (); |
| exit (0); |
| } |