diff options
Diffstat (limited to 'math')
| -rw-r--r-- | math/Makefile | 71 | ||||
| -rw-r--r-- | math/README.libm-test | 1 | ||||
| -rw-r--r-- | math/gen-auto-libm-tests.c | 160 | ||||
| -rwxr-xr-x | math/gen-libm-test.pl | 34 | ||||
| -rw-r--r-- | math/libm-test-driver.c | 50 | ||||
| -rw-r--r-- | math/libm-test-support.c | 25 | ||||
| -rw-r--r-- | math/libm-test-support.h | 27 | ||||
| -rw-r--r-- | math/test-arg-double.h | 25 | ||||
| -rw-r--r-- | math/test-arg-float128.h | 32 | ||||
| -rw-r--r-- | math/test-arg-float32x.h | 28 | ||||
| -rw-r--r-- | math/test-arg-float64.h | 28 | ||||
| -rw-r--r-- | math/test-arg-float64x.h | 32 | ||||
| -rw-r--r-- | math/test-arg-ldouble.h | 31 | ||||
| -rw-r--r-- | math/test-double.h | 1 | ||||
| -rw-r--r-- | math/test-float.h | 1 | ||||
| -rw-r--r-- | math/test-float128.h | 1 | ||||
| -rw-r--r-- | math/test-float32.h | 1 | ||||
| -rw-r--r-- | math/test-float32x.h | 1 | ||||
| -rw-r--r-- | math/test-float64.h | 1 | ||||
| -rw-r--r-- | math/test-float64x.h | 1 | ||||
| -rw-r--r-- | math/test-math-narrow.h | 26 | ||||
| -rw-r--r-- | math/test-math-scalar.h | 3 | ||||
| -rw-r--r-- | math/test-math-vector.h | 1 | ||||
| -rw-r--r-- | math/test-narrow-macros.c | 56 |
24 files changed, 580 insertions, 57 deletions
diff --git a/math/Makefile b/math/Makefile index ee0cd6fce1..42e540cf0e 100644 --- a/math/Makefile +++ b/math/Makefile @@ -145,6 +145,21 @@ test-types = $(test-types-basic) $(type-float128-$(float128-fcts)) \ float32 float64 $(type-float128-$(float128-alias-fcts)) \ float32x $(type-float64x-$(float64x-alias-fcts)) +# Pairs of types for which narrowing functions should be tested (this +# variable has more entries than libm-narrow-types because it includes +# pairs for which the functions sometimes or always alias functions +# for other types). This definition embeds the assumption that if +# _Float64x is supported, so is _Float128, and vice versa (they may or +# may not have the same format). +test-type-pairs = float-double float-ldouble double-ldouble \ + float32-float64 float32-float32x float32x-float64 \ + $(test-type-pairs-f64xf128-$(float128-fcts)) \ + $(test-type-pairs-f64xf128-$(float128-alias-fcts)) +test-type-pairs-f64xf128-yes = float32-float64x float32-float128 \ + float64-float64x float64-float128 \ + float32x-float64x float32x-float128 \ + float64x-float128 + # For each of the basic types (float, double, long double), replace the # occurrences of 'F' in arg 1 with the appropriate suffix for the type. type-basic-foreach = $(foreach t, $(types-basic), \ @@ -219,7 +234,8 @@ tests = test-matherr-3 test-fenv basic-test \ test-femode-traps test-iszero-excess-precision \ test-iseqsig-excess-precision test-flt-eval-method \ test-fp-ilogb-constants test-fp-llogb-constants \ - test-fe-snans-always-signal test-finite-macros $(tests-static) + test-fe-snans-always-signal test-finite-macros test-narrow-macros \ + $(tests-static) tests-static = test-fpucw-static test-fpucw-ieee-static \ test-signgam-uchar-static test-signgam-uchar-init-static \ test-signgam-uint-static test-signgam-uint-init-static \ @@ -268,14 +284,19 @@ libm-test-funcs-noauto = canonicalize ceil cimag conj copysign cproj creal \ scalbln scalbn setpayload setpayloadsig signbit \ significand totalorder totalordermag trunc ufromfp \ ufromfpx +libm-test-funcs-narrow = libm-test-funcs-all = $(libm-test-funcs-auto) $(libm-test-funcs-noauto) libm-test-c-auto = $(foreach f,$(libm-test-funcs-auto),libm-test-$(f).c) libm-test-c-noauto = $(foreach f,$(libm-test-funcs-noauto),libm-test-$(f).c) -generated += libm-test-ulps.h $(libm-test-c-auto) $(libm-test-c-noauto) +libm-test-c-narrow = $(foreach f,$(libm-test-funcs-narrow),\ + libm-test-narrow-$(f).c) +generated += libm-test-ulps.h $(libm-test-c-auto) $(libm-test-c-noauto) \ + $(libm-test-c-narrow) libm-tests-base-normal = $(foreach t,$(test-types),test-$(t)) libm-tests-base-finite = $(foreach t,$(test-types),test-$(t)-finite) libm-tests-base-inline = $(foreach t,$(test-types),test-i$(t)) +libm-tests-base-narrow = $(foreach t,$(test-type-pairs),test-$(t)) libm-tests-base = $(libm-tests-base-normal) $(libm-tests-base-finite) \ $(libm-tests-base-inline) $(libm-vec-tests) libm-tests-normal = $(foreach t,$(libm-tests-base-normal),\ @@ -287,14 +308,18 @@ libm-tests-finite = $(foreach t,$(libm-tests-base-finite),\ libm-tests-inline = $(foreach t,$(libm-tests-base-inline),\ $(foreach f,$(libm-test-funcs-all),\ $(t)-$(f))) +libm-tests-narrow = $(foreach t,$(libm-tests-base-narrow),\ + $(foreach f,$(libm-test-funcs-narrow),\ + $(t)-$(f))) libm-tests-vector = $(foreach t,$(libmvec-tests),\ $(foreach f,$($(t)-funcs),test-$(t)-$(f))) libm-tests = $(libm-tests-normal) $(libm-tests-finite) $(libm-tests-inline) \ - $(libm-tests-vector) + $(libm-tests-narrow) $(libm-tests-vector) libm-tests-for-type = $(foreach f,$(libm-test-funcs-all),\ test-$(1)-$(f) test-$(1)-finite-$(f) \ test-i$(1)-$(f)) \ - $(filter test-$(1)-%,$(libm-tests-vector)) + $(filter test-$(1)-%,$(libm-tests-vector) \ + $(libm-tests-narrow)) libm-tests.o = $(addsuffix .o,$(libm-tests)) @@ -304,6 +329,7 @@ generated += $(addsuffix .c,$(libm-tests)) \ libm-test-c-auto-obj = $(addprefix $(objpfx),$(libm-test-c-auto)) libm-test-c-noauto-obj = $(addprefix $(objpfx),$(libm-test-c-noauto)) +libm-test-c-narrow-obj = $(addprefix $(objpfx),$(libm-test-c-narrow)) $(libm-test-c-noauto-obj): $(objpfx)libm-test%.c: libm-test%.inc \ gen-libm-test.pl @@ -315,6 +341,12 @@ $(libm-test-c-auto-obj): $(objpfx)libm-test%.c: libm-test%.inc \ auto-libm-test-out% $(make-target-directory) $(PERL) gen-libm-test.pl -c $< -a auto-libm-test-out$* -C $@ + +$(libm-test-c-narrow-obj): $(objpfx)libm-test%.c: libm-test%.inc \ + gen-libm-test.pl \ + auto-libm-test-out% + $(make-target-directory) + $(PERL) gen-libm-test.pl -c $< -a auto-libm-test-out$* -C $@ endif ifdef PYTHON @@ -457,6 +489,22 @@ $(foreach t,$(libm-tests-inline),$(objpfx)$(t).c): $(objpfx)test-i%.c: echo "#include <libm-test-$$func.c>"; \ ) > $@ +$(foreach t,$(libm-tests-narrow),$(objpfx)$(t).c): $(objpfx)test-%.c: + type_pair_func=$*; \ + type_pair=$${type_pair_func%-*}; \ + func=$${type_pair_func##*-}; \ + ret_type=$${type_pair%%-*}; \ + arg_type=$${type_pair#*-}; \ + ( \ + echo "#include <test-$$ret_type.h>"; \ + echo "#include <test-arg-$$arg_type.h>"; \ + echo "#include <test-math-no-finite.h>"; \ + echo "#include <test-math-no-inline.h>"; \ + echo "#include <test-math-errno.h>"; \ + echo "#include <test-math-narrow.h>"; \ + echo "#include <libm-test-narrow-$$func.c>"; \ + ) > $@ + $(foreach t,$(libm-tests-vector),$(objpfx)$(t).c): $(objpfx)test-%.c: type_func=$*; \ type=$${type_func%-*}; \ @@ -486,6 +534,14 @@ object-suffixes-left := $(libm-tests-base) include $(o-iterator) define o-iterator-doit +$(foreach f,$(libm-test-funcs-narrow),\ + $(objpfx)$(o)-$(f).o): $(objpfx)$(o)%.o: \ + $(objpfx)libm-test-narrow%.c +endef +object-suffixes-left := $(libm-tests-base-narrow) +include $(o-iterator) + +define o-iterator-doit $(foreach f,$(libm-test-funcs-all),\ $(objpfx)$(o)-$(f).o): CFLAGS += $(libm-test-no-inline-cflags) endef @@ -507,6 +563,13 @@ object-suffixes-left := $(libm-tests-base-inline) include $(o-iterator) define o-iterator-doit +$(foreach f,$(libm-test-funcs-narrow),\ + $(objpfx)$(o)-$(f).o): CFLAGS += $(libm-test-no-inline-cflags) +endef +object-suffixes-left := $(libm-tests-base-narrow) +include $(o-iterator) + +define o-iterator-doit $(foreach f,$($(o)-funcs),\ $(objpfx)test-$(o)-$(f).o): CFLAGS += $(libm-test-vec-cflags) endef diff --git a/math/README.libm-test b/math/README.libm-test index 0271f83f3b..41702c6537 100644 --- a/math/README.libm-test +++ b/math/README.libm-test @@ -120,6 +120,7 @@ parameter. The accepted parameter types are: - "f" for FLOAT - "j" for long double. +- "a" for ARG_FLOAT, the argument type for narrowing functions. - "b" for boolean - just tests if the output parameter evaluates to 0 or 1 (only for output). - "c" for complex. This parameter needs two values, first the real, diff --git a/math/gen-auto-libm-tests.c b/math/gen-auto-libm-tests.c index 934c64bc76..442a10d5e6 100644 --- a/math/gen-auto-libm-tests.c +++ b/math/gen-auto-libm-tests.c @@ -25,6 +25,15 @@ gen-auto-libm-tests auto-libm-test-in <func> auto-libm-test-out-<func> + to generate results for normal libm functions, or + + gen-auto-libm-tests --narrow auto-libm-test-in <func> \ + auto-libm-test-out-narrow-<func> + + to generate results for a function rounding results to a narrower + type (in the case of fma and sqrt, both output files are generated + from the same test inputs). + The input file auto-libm-test-in contains three kinds of lines: Lines beginning with "#" are comments, and are ignored, as are @@ -120,7 +129,22 @@ missing or spurious, or because the calculation of correct results indicated it was optional). Conditions "before-rounding" and "after-rounding" indicate tests where expectations for underflow - exceptions depend on how the architecture detects tininess. */ + exceptions depend on how the architecture detects tininess. + + For functions rounding their results to a narrower type, the format + given on an output test line is the result format followed by + information about the requirements on the argument format to be + able to represent the argument values, in the form + "format:arg_fmt(MAX_EXP,NUM_ONES,MIN_EXP,MAX_PREC)". Instead of + separate lines for separate argument formats, an output test line + relates to all argument formats that can represent the values. + MAX_EXP is the maximum exponent of a nonzero bit in any argument, + or 0 if all arguments are zero; NUM_ONES is the maximum number of + leading bits with value 1 in an argument with exponent MAX_EXP, or + 0 if all arguments are zero; MIN_EXP is the minimum exponent of a + nonzero bit in any argument, or 0 if all arguments are zero; + MAX_PREC is the maximum precision required to represent all + arguments, or 0 if all arguments are zero. */ #define _GNU_SOURCE @@ -1718,12 +1742,13 @@ output_generic_value (FILE *fp, const char *filename, const generic_value *v, } } -/* Generate test output to FP (name FILENAME) for test function TF, - input test IT, choice of input values INPUTS. */ +/* Generate test output to FP (name FILENAME) for test function TF + (rounding results to a narrower type if NARROW), input test IT, + choice of input values INPUTS. */ static void output_for_one_input_case (FILE *fp, const char *filename, test_function *tf, - input_test *it, generic_value *inputs) + bool narrow, input_test *it, generic_value *inputs) { bool long_bits_matters = false; bool fits_long32 = true; @@ -1794,24 +1819,81 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf, mpfr_t res[rm_num_modes]; unsigned int exc_before[rm_num_modes]; unsigned int exc_after[rm_num_modes]; + bool have_fp_arg = false; + int max_exp = 0; + int num_ones = 0; + int min_exp = 0; + int max_prec = 0; for (size_t i = 0; i < tf->num_args; i++) { if (inputs[i].type == gtype_fp) { - round_real (res, exc_before, exc_after, inputs[i].value.f, - f); - if (!mpfr_equal_p (res[rm_tonearest], inputs[i].value.f)) - fits = false; - for (rounding_mode m = rm_first_mode; m < rm_num_modes; m++) - mpfr_clear (res[m]); - if (!fits) - break; + if (narrow) + { + if (mpfr_zero_p (inputs[i].value.f)) + continue; + assert (mpfr_regular_p (inputs[i].value.f)); + int this_exp, this_num_ones, this_min_exp, this_prec; + mpz_t tmp; + mpz_init (tmp); + mpfr_exp_t e = mpfr_get_z_2exp (tmp, inputs[i].value.f); + if (mpz_sgn (tmp) < 0) + mpz_neg (tmp, tmp); + size_t bits = mpz_sizeinbase (tmp, 2); + mp_bitcnt_t tz = mpz_scan1 (tmp, 0); + this_min_exp = e + tz; + this_prec = bits - tz; + assert (this_prec > 0); + this_exp = this_min_exp + this_prec - 1; + assert (this_exp + == mpfr_get_exp (inputs[i].value.f) - 1); + this_num_ones = 1; + while ((size_t) this_num_ones < bits + && mpz_tstbit (tmp, bits - 1 - this_num_ones)) + this_num_ones++; + mpz_clear (tmp); + if (have_fp_arg) + { + if (this_exp > max_exp + || (this_exp == max_exp + && this_num_ones > num_ones)) + { + max_exp = this_exp; + num_ones = this_num_ones; + } + if (this_min_exp < min_exp) + min_exp = this_min_exp; + if (this_prec > max_prec) + max_prec = this_prec; + } + else + { + max_exp = this_exp; + num_ones = this_num_ones; + min_exp = this_min_exp; + max_prec = this_prec; + } + have_fp_arg = true; + } + else + { + round_real (res, exc_before, exc_after, + inputs[i].value.f, f); + if (!mpfr_equal_p (res[rm_tonearest], inputs[i].value.f)) + fits = false; + for (rounding_mode m = rm_first_mode; + m < rm_num_modes; + m++) + mpfr_clear (res[m]); + if (!fits) + break; + } } } if (!fits) continue; - /* The inputs fit this type, so compute the ideal outputs - and exceptions. */ + /* The inputs fit this type if required to do so, so compute + the ideal outputs and exceptions. */ mpfr_t all_res[MAX_NRET][rm_num_modes]; unsigned int all_exc_before[MAX_NRET][rm_num_modes]; unsigned int all_exc_after[MAX_NRET][rm_num_modes]; @@ -1895,10 +1977,21 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf, assert ((merged_exc_before[m] & (1U << exc_underflow)) != 0); } unsigned int merged_exc = merged_exc_before[m]; - if (fprintf (fp, "= %s %s %s%s", tf->name, - rounding_modes[m].name, fp_formats[f].name, - long_cond) < 0) - error (EXIT_FAILURE, errno, "write to '%s'", filename); + if (narrow) + { + if (fprintf (fp, "= %s %s %s%s:arg_fmt(%d,%d,%d,%d)", + tf->name, rounding_modes[m].name, + fp_formats[f].name, long_cond, max_exp, + num_ones, min_exp, max_prec) < 0) + error (EXIT_FAILURE, errno, "write to '%s'", filename); + } + else + { + if (fprintf (fp, "= %s %s %s%s", tf->name, + rounding_modes[m].name, fp_formats[f].name, + long_cond) < 0) + error (EXIT_FAILURE, errno, "write to '%s'", filename); + } /* Print inputs. */ for (size_t i = 0; i < tf->num_args; i++) output_generic_value (fp, filename, &inputs[i], false, @@ -2158,10 +2251,12 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf, generic_value_free (&generic_outputs[i]); } -/* Generate test output data for FUNCTION to FILENAME. */ +/* Generate test output data for FUNCTION to FILENAME. The function + is interpreted as rounding its results to a narrower type if + NARROW. */ static void -generate_output (const char *function, const char *filename) +generate_output (const char *function, bool narrow, const char *filename) { FILE *fp = fopen (filename, "w"); if (fp == NULL) @@ -2177,7 +2272,8 @@ generate_output (const char *function, const char *filename) if (fputs (it->line, fp) < 0) error (EXIT_FAILURE, errno, "write to '%s'", filename); for (size_t k = 0; k < it->num_input_cases; k++) - output_for_one_input_case (fp, filename, tf, it, it->inputs[k]); + output_for_one_input_case (fp, filename, tf, narrow, + it, it->inputs[k]); } } if (fclose (fp) != 0) @@ -2187,14 +2283,30 @@ generate_output (const char *function, const char *filename) int main (int argc, char **argv) { - if (argc != 4) + if (argc != 4 + && !(argc == 5 && strcmp (argv[1], "--narrow") == 0)) error (EXIT_FAILURE, 0, - "usage: gen-auto-libm-tests <input> <func> <output>"); + "usage: gen-auto-libm-tests [--narrow] <input> <func> <output>"); + bool narrow; const char *input_filename = argv[1]; const char *function = argv[2]; const char *output_filename = argv[3]; + if (argc == 4) + { + narrow = false; + input_filename = argv[1]; + function = argv[2]; + output_filename = argv[3]; + } + else + { + narrow = true; + input_filename = argv[2]; + function = argv[3]; + output_filename = argv[4]; + } init_fp_formats (); read_input (input_filename); - generate_output (function, output_filename); + generate_output (function, narrow, output_filename); exit (EXIT_SUCCESS); } diff --git a/math/gen-libm-test.pl b/math/gen-libm-test.pl index 3556817c1d..664fba5909 100755 --- a/math/gen-libm-test.pl +++ b/math/gen-libm-test.pl @@ -165,10 +165,10 @@ sub show_exceptions { } } -# Apply the LIT(x) macro to a literal floating point constant +# Apply the LIT(x) or ARG_LIT(x) macro to a literal floating point constant # and strip any existing suffix. sub _apply_lit { - my ($lit) = @_; + my ($macro, $lit) = @_; my $exp_re = "([+-])?[[:digit:]]+"; # Don't wrap something that does not look like a: # * Hexadecimal FP value @@ -181,7 +181,7 @@ sub _apply_lit { # Strip any existing literal suffix. $lit =~ s/[lLfF]$//; - return "LIT (${lit})"; + return "$macro (${lit})"; } # Apply LIT macro to individual tokens within an expression. @@ -194,7 +194,17 @@ sub apply_lit { my ($lit) = @_; my @toks = split (/ /, $lit); foreach (@toks) { - $_ = _apply_lit ($_); + $_ = _apply_lit ("LIT", $_); + } + return join (' ', @toks); +} + +# Likewise, but apply ARG_LIT for arguments to narrowing functions. +sub apply_arglit { + my ($lit) = @_; + my @toks = split (/ /, $lit); + foreach (@toks) { + $_ = _apply_lit ("ARG_LIT", $_); } return join (' ', @toks); } @@ -228,8 +238,8 @@ sub parse_args { if ($current_arg > 1) { $comma = ', '; } - # FLOAT, long double, int, unsigned int, long int, long long int - if ($descr[$i] =~ /f|j|i|u|l|L/) { + # FLOAT, ARG_FLOAT, long double, int, unsigned int, long int, long long int + if ($descr[$i] =~ /f|a|j|i|u|l|L/) { $call_args .= $comma . &beautify ($args[$current_arg]); ++$current_arg; next; @@ -293,10 +303,12 @@ sub parse_args { $cline = "{ \"$call_args\""; @descr = split //,$descr_args; for ($i=0; $i <= $#descr; $i++) { - # FLOAT, int, long int, long long int - if ($descr[$i] =~ /f|j|i|u|l|L/) { + # FLOAT, ARG_FLOAT, long double, int, unsigned int, long int, long long int + if ($descr[$i] =~ /f|a|j|i|u|l|L/) { if ($descr[$i] eq "f") { $cline .= ", " . &apply_lit ($args[$current_arg]); + } elsif ($descr[$i] eq "a") { + $cline .= ", " . &apply_arglit ($args[$current_arg]); } else { $cline .= ", $args[$current_arg]"; } @@ -420,7 +432,9 @@ sub convert_condition { my (@conds, $ret); @conds = split /:/, $cond; foreach (@conds) { - s/-/_/g; + if ($_ !~ /^arg_fmt\(/) { + s/-/_/g; + } s/^/TEST_COND_/; } $ret = join " && ", @conds; @@ -822,7 +836,7 @@ sub parse_auto_input { chop; next if !/^= /; s/^= //; - if (/^(\S+) (\S+) ([^:]*) : (.*)$/) { + if (/^(\S+) (\S+) ([^: ][^ ]* [^:]*) : (.*)$/) { $auto_tests{$1}{$2}{$3} = $4; } else { die ("bad automatic test line: $_\n"); diff --git a/math/libm-test-driver.c b/math/libm-test-driver.c index b7c0343062..3713e7074f 100644 --- a/math/libm-test-driver.c +++ b/math/libm-test-driver.c @@ -30,6 +30,7 @@ const int flag_test_mathvec = TEST_MATHVEC; #define STRX(x) #x #define STR(x) STRX (x) #define STR_FLOAT STR (FLOAT) +#define STR_ARG_FLOAT STR (ARG_FLOAT) #define STR_VEC_LEN STR (VEC_LEN) /* Informal description of the functions being tested. */ @@ -39,6 +40,8 @@ const int flag_test_mathvec = TEST_MATHVEC; # define TEST_MSG "testing " STR_FLOAT " (inline functions)\n" #elif TEST_FINITE # define TEST_MSG "testing " STR_FLOAT " (finite-math-only)\n" +#elif TEST_NARROW +# define TEST_MSG "testing " STR_FLOAT " (argument " STR_ARG_FLOAT ")\n" #else # define TEST_MSG "testing " STR_FL |
